Skip to content

SMS++ Project Developer's Guide

dmeoli edited this page Jun 19, 2026 · 10 revisions

This guide orients new developers and maintainers of the SMS++ framework: how the project is laid out as an umbrella of git submodules, how to clone and build it for development, where the core abstractions live, the high-level model that ties everything together, and how to contribute.

If you just want to install and use SMS++, follow the Installation guide and the Getting started tutorial first. The complete API is documented in the SMS++ API reference, whose narrative design rationale lives in doxygen/mainpage.md.

[[TOC]]

Project structure

SMS++ is a C++17 framework for modeling and solving block-structured mathematical optimization problems. It is organized as an umbrella project: the SMS++ Project repository is composed almost entirely of git submodules, each a separate repository. The umbrella itself only tracks which commit of each submodule is currently in use, produces unified documentation, and hosts project-wide issues.

The submodules fall into a few groups:

  • Core librarySMS++/, the framework itself (the Block, Solver, Variable, Constraint, Objective, Function, Configuration and Solution abstractions).
  • Blocks — problem-specific models, e.g. BinaryKnapsackBlock, UCBlock, MCFBlock, MMCFBlock, LukFiBlock, StochasticBlock, SDDPBlock, CapacitatedFacilityLocationBlock, InvestmentBlock, TwoStageStochasticBlock, MultiStageStochasticBlock, PolyhedralFunctionBlock.
  • Solvers — algorithm implementations, e.g. MILPSolver (wrapping CPLEX, Gurobi, SCIP and HiGHS), BundleSolver, LagrangianDualSolver, FrankWolfeSolver, BendersDecompositionSolver, MCFClassSolver, MCFLemonSolver, BranchAndXSolver.
  • Supporttools/ (command-line utilities), tests/ (system tests), vcpkg-registry/ (Windows packaging) and the pySMSpp Python interface.

Each Block/Solver submodule follows the same internal layout: include/, src/, lib/, test/, tools/, a CMakeLists.txt and makefiles.

Note: the canonical list of submodules and their repository URLs is the umbrella's .gitmodules file. The top-level CMakeLists.txt exposes one BUILD_* flag per optional module (see Customize the configuration).

The Block-tree, Modification and Solver model

At its heart, SMS++ represents an optimization problem as a tree of Blocks. A Block is "a part of a model": it exposes Variables, Constraints and an Objective, and it can nest arbitrarily many inner Blocks, so structure in the problem maps directly onto structure in the model.

A Solver is an abstract algorithm that attaches to a Block. Several Solvers can be attached to the same Block at once, and different Solvers may exploit either the abstract representation (Variables/Constraints/Objective) or a Block's physical representation (a specialized data structure for fast, structure-exploiting algorithms).

The glue between them is the Modification object. Whenever anything in a Block changes, the Block emits a Modification that is communicated to every attached Solver. This lets a Solver reuse previous solution information and re-optimize efficiently instead of solving from scratch.

The core abstractions all live in SMS++/include/:

Abstraction Key headers
Block Block.h, AbstractBlock.h
Solver Solver.h, CDASolver.h
Variable Variable.h, ColVariable.h
Constraint Constraint.h, RowConstraint.h, FRowConstraint.h, OneVarConstraint.h
Objective Objective.h, FRealObjective.h
Function Function.h, LinearFunction.h, DQuadFunction.h, C05Function.h, C15Function.h, PolyhedralFunction.h, LagBFunction.h, BendersBFunction.h
Configuration Configuration.h, BlockConfig.h, BlockSolverConfig.h
Solution Solution.h, ColVariableSolution.h, RowConstraintSolution.h, ColRowSolution.h
Modification Modification.h

For the full design rationale and the per-class documentation, read doxygen/mainpage.md and browse the SMS++ API reference.

Cloning for development

Clone the umbrella with its submodules and check out the develop branch, which is the canonical entry point for development (the master branch lags behind):

git clone -b develop --recurse-submodules https://gitlab.com/smspp/smspp-project.git
cd smspp-project
git submodule sync --recursive
git submodule update --init --recursive

If you only need a few modules, clone without --recurse-submodules and initialize only what you want:

git clone -b develop https://gitlab.com/smspp/smspp-project.git
cd smspp-project
git submodule update --init SMS++ MILPSolver tools   # etc.

Note: each submodule is checked out at the commit pinned by the umbrella, which leaves it in a detached HEAD state. Before committing inside a submodule, cd into it and check out its develop branch so your work lands on a branch rather than on a detached commit.

To pull the latest tracked commits later:

git pull --recurse-submodules
git submodule sync --recursive
git submodule update --init --recursive

To advance every submodule to the tip of its tracked branch:

git submodule update --remote

Building

The primary build system is CMake; each module also ships a makefile for incremental work. From the umbrella:

mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build . -- -j$(nproc)

Enable optional modules and tests with -D flags (each auto-enables its dependencies):

cmake -DBUILD_UCBlock=ON -DBUILD_MILPSolver=ON -DBUILD_tests=ON ..

For the full set of BUILD_* flags, build-type/shared-library options, compiler flags and output directories, see Customize the configuration. For dependencies and the one-shot INSTALL scripts, see the Installation guide.

The makefile route is described in the umbrella and per-module READMEs: external library paths default to extlib/makefile-default-paths-<os> and can be overridden in a gitignored extlib/makefile-paths.

Running the tests

SMS++ uses two kinds of tests:

  • Unit tests live in the test/ directory of each module and exercise that module in isolation (or together with dependencies it already requires).
  • System tests live in the tests/ submodule and exercise the interaction between otherwise-independent modules (e.g. solving a UCBlock with MILPSolver). They are kept separate so that no module gains a horizontal dependency on another just for testing.

A rule of thumb on where to put a new test: if it uses only your module (and its existing requirements), make it a unit test in your module; if it also needs an independent module, make it a system test in tests/. The same logic applies to new command-line utilities (module-local tools/ vs. the shared tools/ submodule).

Build with -DBUILD_tests=ON, then run them through CTest:

ctest -V -C Release            # all tests
ctest -V -R <test-name>        # a single test by name pattern

Some suites also provide a ./batch script for comprehensive runs. See the tests/README.md for a description of every suite.

Contributing

Work belongs in the relevant submodule repository: the umbrella only records which submodule commit is in use. A typical change therefore is committed (and merged) inside the module, after which a maintainer bumps the submodule reference in the umbrella:

# inside the umbrella, after the submodule has a new commit on its remote
git add <submodule-dir>
git commit -m "updated submodules"

The contribution process itself is defined once, in the project's CONTRIBUTING.md. In short:

  • Discuss the change with the authors first (issue or email).
  • Develop on a feature branch off develop; keep develop at least compiling so you don't disrupt other developers.
  • Open a Merge Request. Before requesting a merge: remove build/temporary files, update the module's CHANGELOG.md (Keep a Changelog format), and bump the version number — including in the CMake files — following Semantic Versioning (MAJOR for incompatible API changes, MINOR for backwards-compatible features, PATCH for backwards-compatible fixes).
  • A project maintainer signs off and merges; the changelog feeds straight into the release notes.

Please read the full CONTRIBUTING.md (it also contains the Code of Conduct) before submitting changes.

Continuous integration

SMS++ runs its builds and tests through GitLab CI/CD. For how the pipelines are configured, the runners involved, and how to work with them, see the GitLab CI/CD guide.

Getting help

For help with a single module, see the Getting help section of its README. For project-level questions, or to propose a new module, open a new issue.

Clone this wiki locally