Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

SparseLevenbergMarquardtOptimizer

Overview

gtsam::cuda::SparseLevenbergMarquardtOptimizer is a GPU-accelerated Levenberg-Marquardt optimizer for general factor graphs — any graph whose factors linearize to finite, unconstrained JacobianFactors. It is a drop-in alternative to LevenbergMarquardtOptimizer: it takes an ordinary NonlinearFactorGraph and Values and returns ordinary Values.

The work is split between host and device:

StageRuns on
Factor linearization and sparse-Jacobian packingCPU (parallel)
Jacobian numerics transferHost → device
Transpose update and normal-equation operatorGPU
Linear solve (cuDSS or PCG)GPU
Retraction and nonlinear trial-error evaluationCPU

The graph topology is compiled once into a reusable sparse-Jacobian plan; the CSR structure and per-factor write offsets are then reused on every iteration, and only the numerics are refreshed. This is why the optimizer is a batch optimizer with fixed topology.

For BAL-style bundle adjustment specifically, prefer the fully GPU-resident SfmLevenbergMarquardtOptimizer, which keeps retraction and error evaluation on the device as well.

Build requirements

Both options are OFF by default, so a standard GTSAM build is unaffected and this class is not compiled at all.

CMake optionEffect
GTSAM_ENABLE_CUDA=ONCompiles the CUDA layer and makes this optimizer available. Defines GTSAM_ENABLE_CUDA=1 publicly. Requires the CUDA Toolkit and CMake ≥ 3.17. Sufficient for the PCG backend.
GTSAM_ENABLE_CUDSS=ONAdditionally enables the cuDSS sparse direct backend and defines GTSAM_ENABLE_CUDSS=1. Requires GTSAM_ENABLE_CUDA=ON (configuration fails otherwise) and a cuDSS installation.
# PCG only — CUDA Toolkit is enough
cmake -S . -B build-cuda -DGTSAM_ENABLE_CUDA=ON

# also enable the cuDSS direct solver
cmake -S . -B build-cuda -DGTSAM_ENABLE_CUDA=ON -DGTSAM_ENABLE_CUDSS=ON

If cuDSS is outside the CUDA or Conda search paths, pass CUDSS_INCLUDE_DIR and CUDSS_LIBRARY explicitly.

Choosing this optimizer

Two independent selections are involved, and it is worth being explicit about them because neither is obvious from the class name alone.

1. The parameters type selects the optimizer. Following the usual GTSAM convention, SparseLevenbergMarquardtParams declares

using OptimizerType = SparseLevenbergMarquardtOptimizer;

so passing SparseLevenbergMarquardtParams is what selects the CUDA path — in exactly the same way that LevenbergMarquardtParams selects LevenbergMarquardtOptimizer and GaussNewtonParams selects GaussNewtonOptimizer. This also means the optimizer can be dropped into templates that dispatch on the parameters type, such as GncParams<SparseLevenbergMarquardtParams>.

2. The linear.backend field selects the linear solver. The optimizer itself is always LM; params.linear.backend chooses how each damped normal system is solved on the device.

Because SparseLevenbergMarquardtParams derives from LevenbergMarquardtParams, the usual convergence tolerances, lambda policy, maximum iteration count, and iteration hook all remain available and are honored by the CUDA path.

Usage (C++)

#include <gtsam/nonlinear/cuda/SparseLevenbergMarquardt.h>

using namespace gtsam;
using namespace gtsam::cuda;

NonlinearFactorGraph graph = /* ... */;
Values initial = /* ... */;

SparseLevenbergMarquardtParams params;
params.linear.backend = LinearSolverType::Pcg;
params.pcg.relativeTolerance = 1e-6;

SparseLevenbergMarquardtOptimizer optimizer(graph, initial, params);
const Values& result = optimizer.optimize();
const auto& diagnostics = optimizer.result();

Solver backends

LinearSolverTypeBehaviorRequires
Cudss (default)Forms the sparse normal matrix and solves it with the cuDSS sparse direct solver. Symbolic analysis is computed once and reused.GTSAM_ENABLE_CUDSS
PcgApplies the JᵀJ + λD operator matrix-free via two sparse matrix-vector products; the normal matrix is never formed. Accuracy and runtime depend on the tolerance, iteration cap, and preconditioner quality.GTSAM_ENABLE_CUDA

DenseCholesky is rejected: the general frontend produces sparse or operator-based systems only.

A user-supplied Ordering is meaningful only for cuDSS, where it is expanded from variable keys to scalar columns. Supplying one with the PCG backend throws, since PCG never factors the matrix. In practice cuDSS automatic ordering has been the most consistent choice on pose and stereo graphs.

Parameters

CUDA-specific fields, layered on top of everything inherited from LevenbergMarquardtParams:

ParameterTypeDefaultDescription
linear.backendLinearSolverTypeCudssDevice linear solver backend
pcg.maxIterationsint250PCG iteration cap
pcg.relativeTolerancedouble1e-6PCG relative residual tolerance
pcg.warmStartbooltrueReuse the previous solution as the initial guess
pcg.convergenceCheckIntervalint10Iterations between host-side convergence checks
fallbackOnUnsupportedbooltrueFall back to CPU LM for unsupported graphs instead of throwing
collectTimingboolfalsePopulate per-stage timings in the result
collectAttemptTraceboolfalseRecord a per-lambda-attempt trace
validateStructureEveryIterationboolfalseRe-validate the plan against the graph each iteration (debugging)

Set fallbackOnUnsupported = false when a caller requires GPU execution and should see an error rather than a silent CPU fallback.

Diagnostics

optimizer.result() returns a SparseLevenbergMarquardtResult reporting, among other fields:

  • backendDevice or CpuFallback, plus the reason for any fallback;

  • the final objective and termination reason;

  • accepted iterations and lambda attempts;

  • symbolic-analysis, factorization, and solve counts;

  • total PCG iterations, iteration-cap hits, and breakdowns;

  • the applied scalar permutation, when an ordering was supplied;

  • host-to-device and device-to-host transfer counts;

  • optional per-stage and per-attempt timings when collectTiming is set.

The timing fields document which measurements overlap; they are not an exclusive partition of wall time and must not be summed as one.

Limitations

  • CUDA support is experimental and opt-in.

  • Batch optimizer with fixed graph topology; optimize() is called once.

  • Requires factors that linearize to finite, unconstrained JacobianFactors.

  • Factor linearization, retraction, and nonlinear error evaluation stay on the CPU.

  • User-supplied orderings apply only to cuDSS.

  • PCG performance is topology- and conditioning-dependent.

  • Small graphs are often faster on the CPU, which does not pay CUDA setup cost.

Benchmarking

The timeCudaSparseLM timing executable compares CPU LM against the available GPU backends and checks final-objective agreement. It is built for every CUDA configuration; cuDSS-specific options appear only when cuDSS is enabled.

Files

Open In Colab