# MATLAB Wrapper This is the GTSAM MATLAB toolbox, a MATLAB wrapper around the GTSAM C++ library. To build it, enable `GTSAM_INSTALL_MATLAB_TOOLBOX=ON` in CMake. The interface to the toolbox is generated automatically by the wrap tool which directly parses C++ header files. The tool generates matlab proxy objects together with all the native functions for wrapping and unwrapping scalar and non scalar types and objects. The interface generated by the wrap tool also redirects the standard output stream (cout) to matlab's console. ## Building the toolbox For non-developers, the recommended installation is outside the build tree. Enable the MATLAB toolbox, build normally, and let `cmake --install .` install both the shared libraries and the toolbox under the install prefix: ```sh cd /path/to/gtsam/build cmake .. \ -DCMAKE_INSTALL_PREFIX=/opt/gtsam \ -DGTSAM_INSTALL_MATLAB_TOOLBOX=ON \ -DMatlab_ROOT_DIR=/Applications/MATLAB_R2025b.app cmake --build . -j6 cmake --install . ``` With that configuration: - GTSAM libraries install under `/opt/gtsam/lib` - the MATLAB toolbox installs under `/opt/gtsam/gtsam_toolbox` - end users add only `/opt/gtsam/gtsam_toolbox` to the MATLAB path This is the best practice for packaging, lab machines, and general non-developer use. It keeps MATLAB pointed at a stable installed toolbox instead of a changing build tree. That being said, if you need a different toolbox location, you can set `GTSAM_TOOLBOX_INSTALL_PATH`. ## Adding the toolbox to your MATLAB path To get started, first add the `toolbox` (or `gtsam_toolbox`) folder to your MATLAB path - in the MATLAB file browser, right-click on the folder and click 'Add to path -> This folder' (**do not add the subfolders to your path**). ## Trying out the examples The examples are located in the 'gtsam_examples' subfolder. You may either run them individually at the MATLAB command line, or open the GTSAM example GUI by running 'gtsamExamples'. Example: ```matlab >> cd /opt/gtsam/gtsam_toolbox % Change to wherever you installed the toolbox >> cd gtsam_examples % Change to the examples directory >> gtsamExamples % Run the GTSAM examples GUI ``` ## Running the unit tests The GTSAM MATLAB toolbox also has a small set of unit tests located in the gtsam_tests directory. Example: ```matlab >> cd /opt/gtsam/gtsam_toolbox % Change to wherever you installed the toolbox >> cd gtsam_tests % Change to the examples directory >> test_gtsam % Run the unit tests Starting: testJacobianFactor Starting: testKalmanFilter Starting: testLocalizationExample Starting: testOdometryExample Starting: testPlanarSLAMExample Starting: testPose2SLAMExample Starting: testPose3SLAMExample Starting: testSFMExample Starting: testStereoVOExample Starting: testVisualISAMExample Tests complete! ``` ## Writing your own code Coding for the GTSAM MATLAB toolbox is straightforward and very fast once you understand a few basic concepts! Please see the [manual](https://borglab.github.io/gtsam/) to get started. ## Filename Case Sensitivity Due to the file name syntax requirements of Matlab, having the files `Symbol.m` (for the class) and `symbol.m` (for the function) together on an OS with a case-insensitive filesystem is impossible. To put it simply, for an OS like Windows or MacOS where the filesystem is case-insensitive, we cannot have two files `symbol.m` and `Symbol.m` in the same folder. When trying to write one file after another, they will end up overwriting each other. We cannot specify 2 different filenames, since Matlab's syntax prevents that. For this reason, on Windows and MacOS, we ignore the `Symbol` class and request users to use the `symbol` function exclusively for key creation. ## Developer-only build-tree install Installing into the build tree is convenient while iterating on the MATLAB wrapper itself: ```sh cd /path/to/gtsam/build cmake .. \ -DGTSAM_INSTALL_MATLAB_TOOLBOX=ON \ -DMatlab_ROOT_DIR=/Applications/MATLAB_R2025b.app \ -DGTSAM_TOOLBOX_INSTALL_PATH=/path/to/gtsam/build/gtsam_toolbox cmake --build . -j6 --target gtsam_matlab_wrapper cmake --install . ``` After rebuilding or reinstalling the toolbox, restart MATLAB and add the toolbox path again. MATLAB caches class and mex state aggressively, and a restart avoids stale wrapper state from an older build. For instructions on updating the version of the [wrap library](https://github.com/borglab/wrap) included in GTSAM to the latest version, please refer to the [wrap README](https://github.com/borglab/wrap/blob/master/README.md#git-subtree-and-contributing) ## Known Issues ### macOS stability note On current macOS + MATLAB R2025b configurations, we have observed MATLAB exit crashes after running nonlinear optimization when GTSAM is built with TBB enabled. If you are building the MATLAB toolbox for macOS, the current recommended setting is: ```sh -DGTSAM_WITH_TBB=OFF ``` This avoids a reproducible shutdown crash in the MATLAB wrapper after optimization. If you do not need TBB elsewhere in your build, it is safest to disable it for MATLAB users. ### Further setup on Linux For a normal install created with `cmake --install .`, the MATLAB mex wrapper is installed with an rpath back to the GTSAM library directory. In the common case where the toolbox and the GTSAM shared libraries stay under the same install prefix, no extra Linux linker setup should be necessary. You may still need manual linker configuration if you: - relocate the toolbox after installation - relocate the GTSAM shared libraries after installation - use an older toolbox build that predates the current rpath handling - run on an older MATLAB/Linux combination with nonstandard loader behavior For a system-wide install under a standard library location, `sudo ldconfig` may be sufficient. For a custom library location, you can point the loader at the installed GTSAM libraries with `LD_LIBRARY_PATH`. If your install prefix is ``, the relevant directory is usually `/lib`: ```sh export LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH ``` ## MATLAB CustomFactor User Guide The MATLAB toolbox supports callback-backed `CustomFactor` objects with the same basic usage pattern as Python: ```matlab import gtsam.* key = 42; model = noiseModel.Unit.Create(1); factor = CustomFactor(model, key, @myErrorFunction); graph = NonlinearFactorGraph; graph.add(factor); ``` The callback signature is: ```matlab function varargout = myErrorFunction(this, values) % Retrieve keys from the factor object ('this') keys = this.keys(); residual = values.atVector(keys.at(0)) - [3]; if nargout > 1 varargout{1} = residual; varargout{2} = {1}; else varargout{1} = residual; end end ``` Rules for MATLAB `CustomFactor` callbacks: - `this` is the MATLAB `gtsam.CustomFactor` object. - `values` is a wrapped `gtsam.Values` object. - The first output must be the unwhitened residual vector. - If Jacobians are requested, the second output must be a cell array with one numeric matrix per factor key, in factor key order. - The residual dimension must match the noise model dimension. - Keys may be passed either as a numeric array or as a `gtsam.KeyVector`. Practical notes: - `unwhitenedError(...)` exercises the residual-only path. - Jacobians should always be verified using numerical derivatives during development. The `gtsam.numericalDerivative` utility is provided for this purpose: ```matlab % Verify Jacobians using numerical derivative expected_H = numericalDerivative.numericalDerivative11(@(v) factor.unwhitenedError(v), initial); actual_H = factor.linearize(initial).jacobian(); EQUALITY('custom jacobian', expected_H, actual_H, 1e-5); ``` Wrapped overloaded methods such as `factor.unwhitenedError` still need an explicit lambda when passed as callbacks in MATLAB. - Optimization usually exercises both the residual and Jacobian paths. - Deleting the MATLAB factor removes its callback from the MATLAB-side registry, so repeated tests in one session do not accumulate stale entries. - Once a MATLAB `CustomFactor` has been constructed, the mex module is intentionally kept loaded for the rest of the MATLAB session to avoid unload-time crashes while callback-backed native factors are still reachable from C++. ## MATLAB CustomFactor Under the Hood The MATLAB `CustomFactor` support is implemented as a MATLAB-specific layer on top of the existing C++ `gtsam::CustomFactor`. Files involved: - [`matlab/custom.i`](custom.i): declares the MATLAB-only native helper so it is included in the generated toolbox. - [`matlab/MatlabCustomFactor.h`](MatlabCustomFactor.h): native adapter class derived from `gtsam::CustomFactor`. - [`matlab/+gtsam/CustomFactor.m`](+gtsam/CustomFactor.m): user-facing MATLAB facade. - [`matlab/+gtsam/customFactorRegistry.m`](+gtsam/customFactorRegistry.m): persistent MATLAB registry that owns callbacks and MATLAB factor handles. Call path: 1. `gtsam.CustomFactor(noiseModel, keys, errorFunc)` registers `errorFunc` in `gtsam.customFactorRegistry` and gets back a numeric callback ID. 2. The MATLAB facade constructs the native `gtsam.MatlabCustomFactor`, passing only the noise model, keys, and callback ID into C++. 3. After construction, the MATLAB facade binds the final MATLAB `gtsam.CustomFactor` object into the registry under the same callback ID. 4. When GTSAM evaluates the factor in C++, `MatlabCustomFactor` calls back into MATLAB with: `gtsam.customFactorRegistry('invoke', callbackId, values)`. 5. The registry looks up the stored MATLAB factor object and function handle, then invokes: `errorFunc(this, values)` or `[error, H] = errorFunc(this, values)`. 6. The native wrapper validates the residual size and Jacobian count, converts the outputs back into GTSAM types, and returns them to the optimizer. Why there is a registry instead of storing the MATLAB function handle directly in C++: - the native factor can be copied and retained by optimizer-owned C++ objects - MATLAB object lifetimes do not match C++ shared pointer lifetimes - storing only a numeric callback ID in C++ avoids keeping raw MATLAB objects in core GTSAM state - the registry lets the callback receive the original MATLAB `gtsam.CustomFactor` object as `this` Debugging checklist for `CustomFactor`: - Constructor fails: verify the toolbox contains both `gtsam.CustomFactor` and `gtsam.MatlabCustomFactor`. - Callback is never called: check that the factor was added to a graph and that the graph path you are exercising actually evaluates it. - Callback gets wrong argument types: inspect `matlab/+gtsam/CustomFactor.m` and `matlab/+gtsam/customFactorRegistry.m` first. - MATLAB reports wrong residual or Jacobian dimensions: the callback contract is being violated; inspect the callback outputs before suspecting the optimizer. - MATLAB crashes or errors while unloading the wrapper: remember that creating a MATLAB `CustomFactor` intentionally locks the mex module for the rest of the session.