MATLAB Interface for OptArrow
OptArrow includes a native MATLAB client under src/matlab.
The interface communicates with the OptArrow HTTP gateway using the Apache Arrow IPC format, supports LP and QP problems via sparse COO triplets, and does not depend on any toolbox-specific schema. Application-specific translation layers belong in the downstream project.
Arrow serialization is handled entirely inside MATLAB using the MATLAB Interface to Apache Arrow — a native C++ add-on built from the Apache Arrow source repository. No Python environment is required.
What Is Included
The MATLAB package provides these entry points under src/matlab/+optarrow:
Function |
Purpose |
|---|---|
|
Configure the active endpoint, engine, backend solver, and timeout |
|
Inspect the active configuration |
|
Serialize an LP/QP payload to Arrow IPC, POST it to the gateway, and return the decoded response struct |
|
Convenience wrapper: accepts a plain MATLAB LP struct, builds the
OptArrow payload, and calls |
|
Convenience wrapper for QP problems (adds the Hessian |
Prerequisites
MATLAB Version
MATLAB R2023b or later is required. optarrow.compute uses
matlab.net.http for HTTP and argument-block validation syntax introduced
in R2023b.
MATLAB Interface to Apache Arrow
The Arrow add-on is not available in MATLAB’s Add-On Explorer. It must be built once from the Apache Arrow source repository.
Build requirements (macOS)
xcode-select --install # C++20 compiler (Apple Clang)
brew install cmake
Build requirements (Linux)
sudo apt install -y cmake build-essential # Debian/Ubuntu
# or: sudo dnf install cmake gcc-c++ # Fedora/RHEL
Step 1 — Build Arrow C++ without S3 support
Building without S3 avoids a shared-library version conflict between MATLAB’s bundled AWS libraries and the AWS C SDK that the default Arrow build pulls in from Anaconda or Homebrew.
git clone https://github.com/apache/arrow.git
cd arrow
cmake -S cpp -B build_cpp \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$HOME/arrow_no_s3 \
-DARROW_S3=OFF \
-DARROW_WITH_RE2=OFF \
-DARROW_CSV=ON \
-DARROW_IPC=ON \
-DARROW_COMPUTE=ON \
-DARROW_BUILD_TESTS=OFF \
-DxsimdSOURCE=BUNDLED
cmake --build build_cpp --config Release -j$(nproc || sysctl -n hw.logicalcpu)
cmake --build build_cpp --config Release --target install
Step 2 — Build the MATLAB bindings against that Arrow
cmake -S matlab -B build_matlab \
-DArrow_DIR=$HOME/arrow_no_s3/lib/cmake/Arrow \
-DCMAKE_INSTALL_PREFIX=$HOME/arrow_matlab
cmake --build build_matlab --config Release
cmake --build build_matlab --config Release --target install
The installer automatically adds $HOME/arrow_matlab/arrow_matlab to
MATLAB’s saved search path (pathdef.m). The next MATLAB session will pick
it up automatically. To use it in the current session:
addpath(genpath(fullfile(getenv('HOME'), 'arrow_matlab', 'arrow_matlab')));
Step 3 — Verify
arrow.recordBatch(table(["A"; "B"], [1; 2], 'VariableNames', {'name', 'val'}))
A successful result returns an arrow.tabular.RecordBatch object.
OptArrow Gateway
The gateway must be running before any MATLAB call:
cd <optArrow_repo>
python src/run_server.py
Default endpoint: http://127.0.0.1:8000/compute
Setup
Add the MATLAB package to the path:
addpath(genpath(fullfile('<optArrow_repo>', 'src', 'matlab')));
Configure the client (call once per session or place in startup.m):
cfg = struct( ...
'endpoint', 'http://127.0.0.1:8000/compute', ...
'engine', 'python', ...
'backendSolver', 'HiGHS', ...
'timeoutSec', 120);
optarrow.setOptArrowConfig(cfg);
Inspect the resolved configuration at any time:
disp(optarrow.getOptArrowConfig())
Solve an LP
optarrow.solveLP accepts a standard LP struct:
Field |
Type |
Meaning |
|---|---|---|
|
sparse or dense matrix |
Constraint matrix (m × n) |
|
double vector |
Right-hand side (length m) |
|
double vector |
Objective coefficients (length n) |
|
double vector |
Variable lower bounds |
|
double vector |
Variable upper bounds |
|
char array or cell of chars |
Per-row sense: |
|
numeric or char |
|
Example — product-mix LP:
optarrow.setOptArrowConfig(struct( ...
'endpoint', 'http://127.0.0.1:8000/compute', ...
'backendSolver', 'HiGHS'));
LP = struct();
LP.A = sparse([20 10; 10 20; 10 30]);
LP.b = [200; 120; 150];
LP.c = [5; 12];
LP.lb = [0; 0];
LP.ub = [1000; 1000];
LP.csense = ['L'; 'L'; 'L'];
LP.osense = -1; % maximise
result = optarrow.solveLP(LP, struct('modelName', 'product_mix'));
fprintf('obj = %.4f\n', result.obj_val);
disp(result.solution)
Solve a QP
optarrow.solveQP extends the LP struct with a symmetric Hessian Q:
QP = struct();
QP.A = sparse([1 1]);
QP.b = 4;
QP.c = [-1; -2];
QP.lb = [0; 0];
QP.ub = [10; 10];
QP.csense = 'L';
QP.osense = 1; % minimise
QP.Q = sparse([2 0; 0 2]); % ½ xᵀQx objective term
result = optarrow.solveQP(QP, struct('modelName', 'qp_demo'));
disp(result.solution)
Q is converted to upper-triangular COO triplets and sent alongside the
LP fields in the same Arrow IPC record batch.
Submit a Generic Payload
Use optarrow.compute directly when you want full control over the request:
payload = struct();
payload.problem_type = 'LP';
payload.engine = 'python';
payload.solver_name = 'HiGHS';
payload.model_name = 'my_lp';
payload.time_limit = 300;
payload.solver_params = struct();
payload.model.A = struct('row', int64([0;1]), 'col', int64([0;1]), ...
'val', [1.0;1.0], 'shape', int64([2,2]));
payload.model.b = [1.0; 1.0];
payload.model.c = [2.0; 3.0];
payload.model.lb = [0.0; 0.0];
payload.model.ub = [10.0; 10.0];
payload.model.csense = {'L'; 'L'};
payload.model.osense = 'max';
result = optarrow.compute(payload);
disp(result)
Response Struct
optarrow.compute always returns a scalar struct with these fields:
Field |
Type |
Meaning |
|---|---|---|
|
logical |
|
|
char |
Solver termination string, e.g. |
|
double |
Numeric status code: |
|
double |
Optimal objective value |
|
double vector |
Primal solution (length n) |
|
double vector |
Dual variables / shadow prices (length m) |
|
double vector |
Reduced costs (length n) |
|
double vector |
Constraint slack |
|
char |
Solver algorithm used, when reported |
|
double |
Wall-clock solve time in seconds, when reported |
Configuration Reference
optarrow.setOptArrowConfig accepts a struct with any of these fields:
Field |
Default |
Meaning |
|---|---|---|
|
|
Gateway URL |
|
|
Backend engine ( |
|
|
Solver name ( |
|
|
Problem class ( |
|
|
Key-value solver parameters |
|
|
HTTP connect + response timeout (seconds) |
|
|
Must be |
All fields have defaults, so passing a partial struct is fine.
Notes
All functions use MATLAB package namespace syntax (
optarrow.compute, not object method dispatch). They live inside the+optarrowfolder.The
transportfield must always be'arrow'; there is no JSON transport path in the MATLAB client.The gateway engine
'python'routes to the in-process Python solver; no separate gRPC process is required.