Skip to content

fhamonic/mippp

Repository files navigation

MIP++

MIP++ (aka MIPpp) is a header‑only C++ library that brings a Pythonic modeling interface to mixed‑integer linear programming. It lets you load multiple solver backends (Gurobi, Cbc, Highs, etc...) at runtime, and uses template metaprogramming to preserve Python‑like syntactic sugar while emitting near‑optimal code at compile time.

Work in progress.

Generic badge Generic badge Generic badge Generic badge

Features

  • Header-only: Dynamically loading the C API of solvers allows MIP++ to stay header-only and easy to integrate to C++ projects.
  • Modern C++20: Utilizes ranges, lambdas, and template metaprogramming for an elegant and efficient API allowing a Pythonic syntax.
  • Solver Agnostic: Supports a variety of solvers (Gurobi, CPLEX, Clp, Cbc, Highs, SoPlex, SCIP, MOSEK, COPT, GLPK and more upcoming) with a unified API.
  • High Performance: Optimized for speed with zero cost abstractions and no unnecessary data copying.

Getting started

Local Conan package (from latest commit)

git clone 
cd mippp
conan create . -u -b=missing -pr=<your_conan_profile>

Then add mippp/<version> as a requirement in your conanfile.txt or conanfile.py.

Conan Center package (upcoming release)

Simply add mippp/1.0.0 to your conanfile.txt or conanfile.py.

Using CMAKE subdirectory

cd <your_project> && mkdir dependencies
git submodule add https://github.com/fhamonic/mippp dependencies/mippp

Then in your CMakeLists.txt:

add_subdirectory(dependencies/mippp)
...
target_link_libraries(<some_target> INTERFACE mippp)

And ensure that your CMake can find the Range-v3 and dylib libraries with find_package calls.

Code examples

Test

#include "mippp/mip_model.hpp"
using namespace fhamonic::mippp;
using namespace fhamonic::mippp::operators;
...
gurobi_api api("gurobi120"); // loads libgurobi120.so C API
gurobi_lp model(api);

auto x1 = model.add_variable();
auto x2 = model.add_variable({.upper_bound=3});

model.set_maximization();
model.set_objective(4 * x1 + 5 * x2);
model.add_constraint(x1 <= 4);
model.add_constraint(2*x1 + x2 <= 9);

model.solve();
auto sol = model.get_solution();

std::cout << std::format("x1: {}, x2: {}", sol[x1], sol[x2]) << std::endl;

Maximum Flow

Using the MELON library, we can express the Maximum Flow problem as

#include "melon/static_digraph.hpp"
using namespace fhamonic::melon;
...
static_graph graph = ...;
arc_map_t<static_graph, double> capacity_map = ...;
vertex_t<static_graph> s = ...;
vertex_t<static_graph> t = ...;
...
gurobi_api api();
gurobi_lp model(api);

auto F = model.add_variable();
auto X_vars = model.add_variables(graph.num_arcs());

model.set_maximization();
model.set_objective(F);

auto flow_conservation_constrs = model.add_constraints(
    graph.vertices(),
    [&](auto && u) {
        return OPT((u == s), xsum(graph.out_arcs(s), X_vars) ==
                            xsum(graph.in_arcs(s), X_vars) + F);
    },
    [&](auto && u) {
        return OPT((u == t), xsum(graph.out_arcs(t), X_vars) ==
                            xsum(graph.in_arcs(t), X_vars) - F);
    },
    [&](auto && u) {
        return xsum(graph.out_arcs(u), X_vars) ==
                xsum(graph.in_arcs(u), X_vars);
    });

for(auto && a : graph.arcs()) {
    model.set_variable_upper_bound(X_vars(a), capacity_map[a]);
}

Shortest Path

static_graph graph = ...;
arc_map_t<static_graph, double> length_map = ...;
vertex_t<static_graph> s = ...;
vertex_t<static_graph> t = ...;
...
gurobi_api api();
gurobi_lp model(api);

auto X_vars = model.add_variables(graph.num_arcs(),
    [](arc_t<static_graph> a) -> std::size_t { return a; });

model.set_maximization();
model.set_objective(xsum(graph.arcs(), [&](auto && a){
    return length_map[a] * X_vars(a);
}));
for(auto && u : graph.vertices()) {
    const double extra_flow = (u == s ? 1 : (u == t ? -1 : 0));
    model.add_constraint(xsum(graph.out_arcs(u), X_vars) == 
                         xsum(graph.in_arcs(u), X_vars) + extra_flow);
}

Sudoku

auto indices = ranges::views::iota(0, 9);
auto values = ranges::views::iota(1, 10);
auto coords = ranges::views::cartesian_product(ranges::views::iota(0, 3),
                                                ranges::views::iota(0, 3));

gurobi_api api;
gurobi_milp model(api);

auto X_vars =
    model.add_binary_variables(9 * 9 * 9, [](int i, int j, int value) {
        return (81 * i) + (9 * j) + (value - 1);
    });

auto single_value_constrs = model.add_constraints(
    ranges::views::cartesian_product(indices, indices), [&](auto && p) {
        auto && [i, j] = p;
        return xsum(values, [&](auto && v) { return X_vars(i, j, v); }) == 1;
    });
auto one_per_row_constrs = model.add_constraints(
    ranges::views::cartesian_product(values, indices), [&](auto && p) {
        auto && [v, i] = p;
        return xsum(indices, [&](auto && j) { return X_vars(i, j, v); }) == 1;
    });
auto one_per_col_constrs = model.add_constraints(
    ranges::views::cartesian_product(values, indices), [&](auto && p) {
        auto && [v, j] = p;
        return xsum(indices, [&](auto && i) { return X_vars(i, j, v); }) == 1;
    });
auto one_per_block_constrs = model.add_constraints(
    ranges::views::cartesian_product(values, coords), [&](auto && p) {
        auto && [v, b] = p;
        return xsum(coords, [&](auto && p2) {
                    return X_vars(3 * std::get<0>(b) + std::get<0>(p2),
                                    3 * std::get<1>(b) + std::get<1>(p2), v);
                }) == 1;
    });

for(auto [i, j, value] : grid_hints) {
    model.set_variable_lower_bound(X_vars(i, j, value), 1);
}

model.solve();
auto solution = model.get_solution();

for(auto i : indices) {
    for(auto j : indices) {
        for(auto v : values) {
            if(solution[X_vars(i, j, v)]) std::cout << ' ' << v;
        }
    }
    std::cout << std::endl;
}

Roadmap

  • Add callbacks for Highs, CPLEX, Cbc, MOSEK and COPT.
  • Add support for XPRS
  • Add special constraints (sos and indicator constraints)
  • Add column generation features

About

Header-only C++20 library for modeling Mixed Integer Programs with multiple solver backends

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages