diff --git a/Clarabel.rs b/Clarabel.rs index 37795e1..ef73652 160000 --- a/Clarabel.rs +++ b/Clarabel.rs @@ -1 +1 @@ -Subproject commit 37795e13874bf560cbc7400552c106688c114c18 +Subproject commit ef736521413f087b1da2bb45a4c5d7ba19bbbe6d diff --git a/README.md b/README.md index 9d7b993..79e2180 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,6 @@ __Clarabel.cpp__ is a C/C++ wrapper of [Clarabel.rs](https://github.com/oxfordco # Installation -> **Note:** The C++ interface is WIP and there might be breaking changes in future releases. - Clarabel.cpp uses CMake to generate the build system and requires the following dependencies: - Rust @@ -53,7 +51,7 @@ git clone --recurse-submodules https://github.com/oxfordcontrol/Clarabel.cpp.git cd Clarabel.cpp ``` -## Build +## Building the solver ```sh mkdir build @@ -82,51 +80,39 @@ where `VCPKG_TOOLCHAIN_PATH` is the path to the vcpkg toolchain file. - For 32-bit platforms, use `x86-windows` instead of `x64-windows`. -## CMake options - -### SDP support - -To enable SDP features, set the `-DCLARABEL_FEATURE_SDP` option to one of the following values: -- `sdp-accelerate` -- `sdp-netlib` -- `sdp-openblas` -- `sdp-mkl` -- `sdp-r` - -By default, `-DCLARABEL_FEATURE_SDP=none` and SDP support is disabled. - -### JSON file input/output support - -To enable reading and writing of problem data to JSON files, set -`-DCLARABEL_FEATURE_SERDE=true`. - -When reporting issues with the solver, it can be helpful to provide a JSON file that reproduces the problem. - -### Alternative linear algebra libraries - -To enable the [faer-rs](https://faer-rs.github.io/) sparse linear algebra library as an additional solver option, set `-DCLARABEL_FEATURE_FAER_SPARSE=true`. - -To enable the [`MKL Pardiso`](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/) sparse linear algebra library as an additional solver option, set `-DCLARABEL_FEATURE_PARDISO_MKL=true`. +## Optional solver features -The solver will dynamically link to the MKL library, which must be accessible via the system library path (e.g. on `LD_LIBRARY_PATH` on Linux). +Clarabel.rs supports a variety of build options for semidefinite program (SDP) support, JSON file read/write, 3rd party linear solvers etc. Feature options can be passed to cargo via CMake using `-DCLARABEL_RUST_FEATURES = "feature1,feature2,..."`. -Alternatively, set the `MKLROOT` environment variable to the root of the MKL installation or `MKL_PARDISO_PATH` to the location of the MKL Pardiso library (e.g. the location of `libmkl_rt.so` in Linux). +| Feature | Description | +|---------|-------------| +| `serde` | enables read/write of problem instances to .json files (enabled by default) | +| `faer-sparse` | enables "faer" as an optional direct_solve_method | +| `pardiso-mkl` | enables "mkl" as an optional direct_solve_method (Intel only) | +| `pardiso-panua` | enables "panua" as an optional direct_solve_method (requires separate license) | +| `pardiso` | enables both the "mkl" and "panua" options | +| `sdp-accelerate` | enables solution of SDPs using Apple's "accelerate" BLAS/LAPACK implementation (OSX only) | +| `sdp-mkl` | enables solution of SDPs using Intel's BLAS/LAPACK implementation (Intel only) | +| `sdp-openblas` | enables solution of SDPs using OpenBlas | +| `sdp-netlib` | enables solution of SDPs using the Netlib reference BLAS/LAPACK (not recommended) | +| `buildinfo` | adds a buildinfo function to the package that reports on the build configuration | -To enable the [`Panua Pardiso`](https://panua.ch/pardiso/) sparse linear algebra library as an additional solver option, set `-DCLARABEL_FEATURE_PARDISO_PANUA=true`. This library is not open source and requires a license. +### Linking to Pardiso +To enable dynamic linking to MKL Pardiso, the MKL Pardiso libary (e.g. `libmkl_rt.so`) must be on the system library path (e.g. on `LD_LIBRARY_PATH` on Linux). Alternatively, set the `MKLROOT` environment variable to the root of the MKL installation or `MKL_PARDISO_PATH` to the location of the library. The Intel MKL library is available as part of the Intel oneAPI toolkit and is only available on x86_64 platforms. -The solver will dynamically link to the Panua Pardiso library, which must be accessible via the system library path (e.g. on `LD_LIBRARY_PATH` on Linux). +To enable dynamic linking to Panua Pardiso, the Panua Pardiso library (e.g. `libpardiso.so`) must be on the system library path (e.g. on `LD_LIBRARY_PATH` on Linux). Alternatively, set the `PARDISO_PATH` environment variable to the location of the library. -Alternatively, set the `PARDISO_PATH` environment variable to the location of the Panua Pardiso library (e.g. the location of `libpardiso.so` in Linux). +Panua Pardiso is a commercial solver and requires a separate license. -### Unit tests +## Unit tests By default, unit tests are disabled to reduce build time. To enable unit tests, set `-DCLARABEL_BUILD_TESTS=true` in cmake. -### Release mode +## Release mode The solver will build the Rust source in debug mode. To build in release mode, set `-DCMAKE_BUILD_TYPE=Release` in cmake. -## Run examples +## Running examples Examples for both C and C++ are available in `examples/c` and `examples/cpp` and can be run from the `build` directory using: diff --git a/rust_wrapper/CMakeLists.txt b/rust_wrapper/CMakeLists.txt index 882bc4b..9d03f76 100644 --- a/rust_wrapper/CMakeLists.txt +++ b/rust_wrapper/CMakeLists.txt @@ -18,82 +18,149 @@ endif() set(CLARABEL_C_OUTPUT_DIR ${clarabel_c_output_directory} PARENT_SCOPE) +if(NOT DEFINED CLARABEL_RUST_FEATURES) + set(CLARABEL_RUST_FEATURES "") +endif() + +# ------------------------------------- +# Cargo feature configuration helper functions +#-------------------------------------- + +# Check if CLARABEL_RUST_FEATURES contains a specific feature +function(rust_feature_is_enabled PATTERN RESULT_VAR) + string(REPLACE "," ";" FEATURE_LIST "${CLARABEL_RUST_FEATURES}") + set(${RESULT_VAR} FALSE PARENT_SCOPE) + foreach(FEATURE ${FEATURE_LIST}) + if(FEATURE MATCHES "${PATTERN}") + set(${RESULT_VAR} TRUE PARENT_SCOPE) + break() + endif() + endforeach() +endfunction() + +# Check if CLARABEL_RUST_FEATURES contains any of the "sdp" options +function(rust_features_has_sdp RESULT_VAR) + if(CLARABEL_RUST_FEATURES MATCHES ".*sdp.*") + set(${RESULT_VAR} TRUE PARENT_SCOPE) + else() + set(${RESULT_VAR} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Helper function to append a feature to a comma-separated list +function(append_feature FEATURE_LIST_VAR NEW_FEATURE) + if(${FEATURE_LIST_VAR} STREQUAL "") + set(${FEATURE_LIST_VAR} "${NEW_FEATURE}" PARENT_SCOPE) + else() + set(${FEATURE_LIST_VAR} "${${FEATURE_LIST_VAR}},${NEW_FEATURE}" PARENT_SCOPE) + endif() +endfunction() + +# Automatically prefix features with 'clarabel/' if not already prefixed +# For serde, add it as both clarabel/serde and serde, with the latter +# necessary for the rust_wrapper cargo configuration. + +function(format_clarabel_rust_features INPUT_FEATURES OUTPUT_VAR) + string(REPLACE "," ";" FEATURE_LIST "${INPUT_FEATURES}") + set(FORMATTED_FEATURES "") + foreach(FEATURE ${FEATURE_LIST}) + # Regular handling: prefix with clarabel/ if not already prefixed + if(NOT FEATURE MATCHES "^clarabel/") + set(FEATURE "clarabel/${FEATURE}") + endif() + append_feature(FORMATTED_FEATURES "${FEATURE}") + endforeach() + set(${OUTPUT_VAR} "${FORMATTED_FEATURES}" PARENT_SCOPE) +endfunction() + # ------------------------------------- -# Cargo features configuration +# Cargo feature configuration backward compatibility #-------------------------------------- -# A list of features to pass to rustc -set(CLARABEL_BUILD_FEATURES "") -# SDP feature flag if(NOT CLARABEL_FEATURE_SDP STREQUAL "none") + append_feature(CLARABEL_RUST_FEATURES "${CLARABEL_FEATURE_SDP}") +endif() + +if(CLARABEL_FEATURE_FAER_SPARSE) + append_feature(CLARABEL_RUST_FEATURES "faer-sparse") +endif() - # Set the Rust feature flag - set(CLARABEL_BUILD_FEATURES "${CLARABEL_BUILD_FEATURES},${CLARABEL_FEATURE_SDP}") +if(CLARABEL_FEATURE_PARDISO_MKL) + append_feature(CLARABEL_RUST_FEATURES "pardiso-mkl") +endif() + +# SERDE feature flag +if(CLARABEL_FEATURE_SERDE) + append_feature(CLARABEL_RUST_FEATURES "serde") +endif() + + +# ------------------------------------- +# Cargo feature configuration +#-------------------------------------- +# ---- +# Rust features should be passed via the CLARABEL_RUST_FEATURES variable. +# For some features, we requires some additional CMake configuration. +# --- + +rust_features_has_sdp(CONFIG_SDP) +if(CONFIG_SDP) # Define the FEATURE_SDP flag for all targets that link against clarabel_c target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_SDP) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_SDP) endif() -# FAER_SPARSE feature flag -if(CLARABEL_FEATURE_FAER_SPARSE) - - # Set the Rust feature flag - set(CLARABEL_BUILD_FEATURES "${CLARABEL_BUILD_FEATURES},faer-sparse") - - # Define the FEATURE_FAER_SPARSE flag for all targets that link against clarabel_c +rust_feature_is_enabled("faer-sparse" CONFIG_FAER_SPARSE) +if(CONFIG_FAER_SPARSE) target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_FAER_SPARSE) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_FAER_SPARSE) endif() -# PARDISO_MKL feature flag -if(CLARABEL_FEATURE_PARDISO_MKL) - - # Set the Rust feature flag - set(CLARABEL_BUILD_FEATURES "${CLARABEL_BUILD_FEATURES},pardiso-mkl") - - # Define the FEATURE_PARDISO_MKL flag for all targets that link against clarabel_c +rust_feature_is_enabled("pardiso-mkl" CONFIG_PARDISO_MKL) +if(CONFIG_PARDISO_MKL) target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_PARDISO_MKL) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_PARDISO_MKL) - endif() -# PARDISO_PANUA feature flag -if(CLARABEL_FEATURE_PARDISO_PANUA) - - # Set the Rust feature flag - set(CLARABEL_BUILD_FEATURES "${CLARABEL_BUILD_FEATURES},pardiso-panua") - - # Define the FEATURE_PARDISO_PANUA flag for all targets that link against clarabel_c +rust_feature_is_enabled("pardiso-panua" CONFIG_PARDISO_PANUA) +if(CONFIG_PARDISO_PANUA) target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_PARDISO_PANUA) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_PARDISO_PANUA) endif() -if(CLARABEL_FEATURE_PARDISO_MKL OR CLARABEL_FEATURE_PARDISO_PANUA) +if(CONFIG_PARDISO_MKL OR CONFIG_PARDISO_PANUA) target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_PARDISO_ANY) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_PARDISO_ANY) endif() - -# SERDE feature flag -if(CLARABEL_FEATURE_SERDE) - - # Set the Rust feature flag - set(CLARABEL_BUILD_FEATURES "${CLARABEL_BUILD_FEATURES},serde") - - # Define the FEATURE_SERDE flag for all targets that link against clarabel_c +rust_feature_is_enabled("serde" CONFIG_SERDE) +if(CONFIG_SERDE) target_compile_definitions(libclarabel_c_static INTERFACE FEATURE_SERDE) target_compile_definitions(libclarabel_c_shared INTERFACE FEATURE_SERDE) endif() +# replace each rust with clarabel/ so that they pass through +# this wrapper to the clarabel crate underneath +format_clarabel_rust_features("${CLARABEL_RUST_FEATURES}" CLARABEL_RUST_FEATURES) +# some features must also be specified at the wrapper level +# add them again, but after prepending clarabel/ to everything +if(CONFIG_SDP) + append_feature(CLARABEL_RUST_FEATURES "sdp") +endif() +if(CONFIG_SERDE) + append_feature(CLARABEL_RUST_FEATURES "serde") +endif() -set(clarabel_c_build_flags "${clarabel_c_build_flags};--features=${CLARABEL_BUILD_FEATURES}") -message("-- Rust feature list: " ${CLARABEL_BUILD_FEATURES}) -message("-- Cargo options: " "${clarabel_c_build_flags}") +if(NOT CLARABEL_RUST_FEATURES STREQUAL "") + set(clarabel_c_build_flags "${clarabel_c_build_flags};--features=${CLARABEL_RUST_FEATURES}") +endif() +message("-- Rust feature list: " ${CLARABEL_RUST_FEATURES}) +message("-- Cargo options: " "${clarabel_c_build_flags}") # ------------------------------------- # ------------------------------------- @@ -118,11 +185,11 @@ add_custom_target( WORKING_DIRECTORY ${CLARABEL_ROOT_DIR}/rust_wrapper # Commands for building the Rust library COMMAND cargo build ${clarabel_c_build_flags} - COMMAND cargo install cbindgen --version 0.24.5 + COMMAND cargo install cbindgen # Generate the C header - COMMAND cbindgen --config cbindgen.toml --crate clarabel_c --output ./headers/clarabel.h --lang c + COMMAND cbindgen --config cbindgen.toml --quiet --crate clarabel_c --output ./headers/clarabel.h --lang c # Generate the C++ header - COMMAND cbindgen --config cbindgen.toml --crate clarabel_c --output ./headers/clarabel.hpp + COMMAND cbindgen --config cbindgen.toml --quiet --crate clarabel_c --output ./headers/clarabel.hpp BYPRODUCTS "${LIBCLARABEL_C_SHARED_PATH}" "${LIBCLARABEL_C_STATIC_PATH}" diff --git a/rust_wrapper/Cargo.toml b/rust_wrapper/Cargo.toml index 9df2b58..c38b417 100644 --- a/rust_wrapper/Cargo.toml +++ b/rust_wrapper/Cargo.toml @@ -18,16 +18,6 @@ lto = true codegen-units = 1 [features] -# Define features for SDP support in Clarabel.rs -sdp = ["clarabel/sdp"] -sdp-accelerate = ["sdp", "clarabel/sdp-accelerate"] -sdp-netlib = ["sdp", "clarabel/sdp-netlib"] -sdp-openblas = ["sdp", "clarabel/sdp-openblas"] -sdp-mkl = ["sdp", "clarabel/sdp-mkl"] -sdp-r = ["sdp", "clarabel/sdp-r"] - -serde = ["dep:serde", "clarabel/serde"] -faer-sparse = ["clarabel/faer-sparse"] -pardiso-mkl = ["clarabel/pardiso-mkl"] -pardiso-panua = ["clarabel/pardiso-panua"] -pardiso = ["clarabel/pardiso"] #chooses both +default = [] +sdp = [] +serde = ["dep:serde"] diff --git a/rust_wrapper/cbindgen.toml b/rust_wrapper/cbindgen.toml index e1afcb3..546432b 100644 --- a/rust_wrapper/cbindgen.toml +++ b/rust_wrapper/cbindgen.toml @@ -12,5 +12,19 @@ documentation = false [defines] "feature = sdp" = "FEATURE_SDP" +"feature = serde" = "FEATURE_SERDE" "feature = faer-sparse" = "FEATURE_FAER_SPARSE" -"feature = serde" = "FEATURE_SERDE" \ No newline at end of file +"feature = pardiso-mkl" = "FEATURE_PARDISO_MKL" +"feature = pardiso-panua" = "FEATURE_PARDISO_PANUA" + +[parse] +parse_deps = true +include = ["clarabel"] +exclude = ["indexmap"] + +[export] +include = [ + "SolverStatusFFI", + "DefaultInfoFFI", + "DefaultSettingsFFI", +] \ No newline at end of file diff --git a/rust_wrapper/src/algebra.rs b/rust_wrapper/src/algebra.rs index 0c26ed4..53ed26e 100644 --- a/rust_wrapper/src/algebra.rs +++ b/rust_wrapper/src/algebra.rs @@ -9,7 +9,7 @@ pub struct ClarabelCscMatrix { pub n: usize, /// CSC format column pointer. /// - /// Ths field should have length `n+1`. The last entry corresponds + /// This field should have length `n+1`. The last entry corresponds /// to the the number of nonzeros and should agree with the lengths /// of the `rowval` and `nzval` fields. pub colptr: *const usize, diff --git a/rust_wrapper/src/core.rs b/rust_wrapper/src/core.rs index d590a61..954b93e 100644 --- a/rust_wrapper/src/core.rs +++ b/rust_wrapper/src/core.rs @@ -19,7 +19,9 @@ pub mod cones { /// The exponential cone in R^3. /// /// This cone takes no parameters - ExponentialConeT(), + /// NB: Just a plain enum variant and not a unit type (i.e. not ExponentialConeT()) + /// as in the clarabel crate to avoid ZST / FFI complaints from cbindgen + ExponentialConeT, /// The power cone in R^3. /// /// The parameter indicates the power. diff --git a/rust_wrapper/src/solver/implementations/default/solver.rs b/rust_wrapper/src/solver/implementations/default/solver.rs index 01632bf..418469c 100644 --- a/rust_wrapper/src/solver/implementations/default/solver.rs +++ b/rust_wrapper/src/solver/implementations/default/solver.rs @@ -7,6 +7,7 @@ use crate::solver::implementations::default::settings::{ ClarabelDefaultSettings, ClarabelDefaultSettings_f32, ClarabelDefaultSettings_f64, }; use crate::utils; +use clarabel::solver::ffi::SolverStatusFFI; use clarabel::algebra::FloatT; use clarabel::io::ConfigurablePrintTarget; @@ -29,7 +30,7 @@ use super::solution::DefaultSolution; pub type ClarabelDefaultSolver_f32 = c_void; pub type ClarabelDefaultSolver_f64 = c_void; -pub type ClarabelSolverStatus = clarabel::solver::ffi::SolverStatusFFI; +pub type ClarabelSolverStatus = SolverStatusFFI; // Wrapper function to create a DefaultSolver object from C using dynamic memory allocation // - Matrices and vectors are constructed from raw pointers diff --git a/rust_wrapper/src/utils/supported_cones_T.rs b/rust_wrapper/src/utils/supported_cones_T.rs index 3e79052..78ed399 100644 --- a/rust_wrapper/src/utils/supported_cones_T.rs +++ b/rust_wrapper/src/utils/supported_cones_T.rs @@ -28,11 +28,11 @@ pub fn convert_from_C_cone(cone: &ClarabelSupportedConeT) -> lib:: ClarabelSupportedConeT::SecondOrderConeT(payload) => { lib::SupportedConeT::SecondOrderConeT(*payload) } - ClarabelSupportedConeT::ExponentialConeT() => lib::SupportedConeT::ExponentialConeT(), + ClarabelSupportedConeT::ExponentialConeT => lib::SupportedConeT::ExponentialConeT(), ClarabelSupportedConeT::PowerConeT(payload) => lib::SupportedConeT::PowerConeT(*payload), - ClarabelSupportedConeT::GenPowerConeT(ptr_alpha,dim1,dim2) => { + ClarabelSupportedConeT::GenPowerConeT(ptr_alpha, dim1, dim2) => { let alpha = unsafe { std::slice::from_raw_parts(*ptr_alpha, *dim1) }; - lib::SupportedConeT::GenPowerConeT(alpha.to_vec(),*dim2) + lib::SupportedConeT::GenPowerConeT(alpha.to_vec(), *dim2) } #[cfg(feature = "sdp")] ClarabelSupportedConeT::PSDTriangleConeT(payload) => {