Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
57 changes: 57 additions & 0 deletions c_api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# MQLib C ABI

This directory defines the native C ABI for solving QUBO instances through
MQLib without invoking the command-line executable.

The files here are intended to be compiled with the upstream MQLib C++ sources
and packaged as a shared library by `MQLib_jll`. The Julia wrapper does not call
this ABI yet; that integration belongs to the follow-up shared-library issue.

## API

Include `c_api/include/mqlib_c_api.h` from C, C++, or Julia `ccall` bindings.
The public ABI exposes only C scalars, pointers, and structs:

- `MQLibCQUBOInput` passes the problem dimension, linear coefficients, sparse
off-diagonal quadratic coordinate arrays, heuristic selection, runtime limit,
and random seed.
- `MQLibCQUBOResult` passes caller-owned buffers for the objective value,
binary solution vector, runtime, selected heuristic, and optional incumbent
history.
- `mqlib_solve_qubo` returns a stable `MQLibCStatus` code. It does not transfer
ownership of any caller buffer.

The wrapper validates ABI inputs before constructing MQLib C++ objects and
reports those failures through status codes. Unrecoverable exits inside
upstream heuristic internals are still upstream behavior until those internals
are made status-code aware.

Quadratic coordinates may be zero-based or one-based, selected by
`index_base`. Diagonal terms belong in `linear`; quadratic arrays must contain
only off-diagonal entries. MQLib interprets off-diagonal entries as the upper
triangle of the symmetric QUBO matrix, matching the existing MQLib `.qubo` file
format.

Set `heuristic` to a QUBO heuristic code such as `ALKHAMIS1998` to run that
heuristic. Set it to `NULL` or an empty string to run the hyperheuristic path.

## Build Sketch

The exact build recipe belongs in `MQLib_jll`, but a local syntax check against
an upstream MQLib checkout looks like:

```sh
c++ -std=c++11 \
-Ic_api/include \
-I/path/to/MQLib/include \
-fsyntax-only \
c_api/src/mqlib_c_api.cpp
```

When building the shared library, compile `c_api/src/mqlib_c_api.cpp` together
with the upstream MQLib implementation sources needed by the heuristics. The
upstream executable entry point (`src/main.cpp`) should not be part of the
shared-library target.

The small native example in `c_api/examples/solve_qubo.c` demonstrates both an
explicit QUBO heuristic and the hyperheuristic call shape.
69 changes: 69 additions & 0 deletions c_api/examples/solve_qubo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <stdio.h>

#include "mqlib_c_api.h"

static int solve_with_heuristic(const char *heuristic) {
const double linear[3] = {5.0, 3.0, 1.0};
const int32_t quadratic_i[2] = {0, 1};
const int32_t quadratic_j[2] = {1, 2};
const double quadratic_value[2] = {-6.0, -1.0};

int32_t solution[3] = {0, 0, 0};
char selected_heuristic[64];
double history_values[32];
double history_times[32];

MQLibCQUBOInput input = {
MQLIB_C_ABI_VERSION,
3,
linear,
2,
quadratic_i,
quadratic_j,
quadratic_value,
MQLIB_C_INDEX_BASE_ZERO,
heuristic,
0.01,
1234
};

MQLibCQUBOResult result = {
MQLIB_C_ABI_VERSION,
0.0,
0.0,
solution,
3,
selected_heuristic,
(int32_t)sizeof(selected_heuristic),
history_values,
history_times,
32,
0
};

const int status = mqlib_solve_qubo(&input, &result);
if (status != MQLIB_STATUS_OK) {
fprintf(stderr, "MQLib failed: %s\n", mqlib_c_status_message(status));
return status;
}

printf(
"%s objective %.15g solution %d %d %d runtime %.6f seconds\n",
result.selected_heuristic,
result.objective_value,
result.solution[0],
result.solution[1],
result.solution[2],
result.runtime_seconds
);
return MQLIB_STATUS_OK;
}

int main(void) {
int status = solve_with_heuristic("ALKHAMIS1998");
if (status != MQLIB_STATUS_OK) {
return status;
}

return solve_with_heuristic(NULL);
}
98 changes: 98 additions & 0 deletions c_api/include/mqlib_c_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#ifndef MQLIB_C_API_H
#define MQLIB_C_API_H

#include <stdint.h>

#define MQLIB_C_ABI_VERSION 1u

#define MQLIB_C_INDEX_BASE_ZERO 0
#define MQLIB_C_INDEX_BASE_ONE 1

#if defined(_WIN32) && defined(MQLIB_C_BUILD_SHARED)
#define MQLIB_C_API __declspec(dllexport)
#elif defined(_WIN32) && defined(MQLIB_C_USE_SHARED)
#define MQLIB_C_API __declspec(dllimport)
#else
#define MQLIB_C_API
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef enum MQLibCStatus {
MQLIB_STATUS_OK = 0,
MQLIB_STATUS_ABI_VERSION_MISMATCH = 1,
MQLIB_STATUS_INVALID_ARGUMENT = 2,
MQLIB_STATUS_INVALID_HEURISTIC = 3,
MQLIB_STATUS_BUFFER_TOO_SMALL = 4,
MQLIB_STATUS_ALLOCATION_FAILED = 5,
MQLIB_STATUS_INTERNAL_ERROR = 6
} MQLibCStatus;

typedef struct MQLibCQUBOInput {
uint32_t abi_version;

int32_t dimension;
const double *linear;

int64_t quadratic_count;
const int32_t *quadratic_i;
const int32_t *quadratic_j;
const double *quadratic_value;
int32_t index_base;

/*
* NULL or an empty string runs the MQLib hyperheuristic. Otherwise this
* must be a valid MQLib QUBO or Max-Cut heuristic code.
*/
const char *heuristic;

double runtime_limit_seconds;
int32_t random_seed;
} MQLibCQUBOInput;

typedef struct MQLibCQUBOResult {
uint32_t abi_version;

double objective_value;
double runtime_seconds;

/*
* Caller-owned buffer with capacity in solution_length on input. The
* required length is written back on output.
*/
int32_t *solution;
int32_t solution_length;

/*
* Optional caller-owned buffer. selected_heuristic_length is the byte
* capacity on input and the required byte count, including NUL, on output.
*/
char *selected_heuristic;
int32_t selected_heuristic_length;

/*
* Optional caller-owned buffers for objective history. history_capacity is
* the input capacity of both arrays. history_length is the required entry
* count on output. If the capacity is too small, the prefix that fits is
* copied and MQLIB_STATUS_BUFFER_TOO_SMALL is returned.
*/
double *history_objective_values;
double *history_times_seconds;
int32_t history_capacity;
int32_t history_length;
} MQLibCQUBOResult;

MQLIB_C_API int mqlib_c_abi_version(void);
MQLIB_C_API const char *mqlib_c_status_message(int status);
MQLIB_C_API int mqlib_solve_qubo(
const MQLibCQUBOInput *input,
MQLibCQUBOResult *result
);

#ifdef __cplusplus
}
#endif

#endif
Loading
Loading