Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Clarabel.rs
Submodule Clarabel.rs updated 1 files
+17 −2 Cargo.toml
58 changes: 22 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:

Expand Down
153 changes: 110 additions & 43 deletions rust_wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <feature> with clarabel/<feature> 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}")

# -------------------------------------
# -------------------------------------
Expand All @@ -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}"
Expand Down
16 changes: 3 additions & 13 deletions rust_wrapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
16 changes: 15 additions & 1 deletion rust_wrapper/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
"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",
]
2 changes: 1 addition & 1 deletion rust_wrapper/src/algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct ClarabelCscMatrix<T = f64> {
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,
Expand Down
4 changes: 3 additions & 1 deletion rust_wrapper/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion rust_wrapper/src/solver/implementations/default/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions rust_wrapper/src/utils/supported_cones_T.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ pub fn convert_from_C_cone<T: FloatT>(cone: &ClarabelSupportedConeT<T>) -> 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) => {
Expand Down