Nonlinear Functions

QUBO++ supports the absolute value qbpp::abs(), ReLU qbpp::relu(), maximum qbpp::max(), and minimum qbpp::min() directly inside expressions. The solvers bundled with QUBO++ handle the function values directly and search efficiently — there is no need to design auxiliary variables or penalty polynomials by hand. These functions can also be combined with Native Integer Variables.

Absolute value: abs

qbpp::abs(f) denotes $|f|$ and qbpp::abs(f, 2) denotes $|f|^2$ (the exponent is 1 or 2). The following program minimizes $|x + y - 13| + |x - y - 3|$:

#include <qbpp/exhaustive_solver.hpp>
#include <qbpp/qbpp.hpp>

int main() {
  auto x = 0 <= qbpp::int_var("x") <= 10;
  auto y = 0 <= qbpp::int_var("y") <= 10;
  auto f = qbpp::abs(x + y - 13) + qbpp::abs(x - y - 3);
  f.simplify_as_binary();
  auto solver = qbpp::ExhaustiveSolver(f);
  auto sol = solver.search();
  std::cout << "x = " << sol(x) << ", y = " << sol(y) << std::endl;
  std::cout << "f = " << sol.energy() << std::endl;
}

The output of the program is as follows:

x = 8, y = 5
f = 0

ReLU: relu

qbpp::relu(f) denotes $\max(0, f)$ and qbpp::relu(f, 2) denotes $\max(0, f)^2$; they are convenient for penalizing only the excess over a threshold. The following program maximizes the profit $4x + 7y$ while quadratically penalizing the workload $2x + 3y$ beyond 36 and imposing the native constraint $x + y \le 12$:

#include <qbpp/exhaustive_solver.hpp>
#include <qbpp/qbpp.hpp>

int main() {
  auto x = 0 <= qbpp::int_var("x") <= 20;
  auto y = 0 <= qbpp::int_var("y") <= 20;
  auto profit = 4 * x + 7 * y;
  auto overtime = qbpp::relu(2 * x + 3 * y - 36, 2);
  auto f = -profit + overtime + 100 * qbpp::cons(x + y <= 12);
  f.simplify_as_binary();
  auto solver = qbpp::ExhaustiveSolver(f);
  auto sol = solver.search();
  std::cout << "x = " << sol(x) << ", y = " << sol(y) << std::endl;
  std::cout << "profit = " << sol(profit) << std::endl;
}

The output of the program is as follows:

x = 0, y = 12
profit = 84

Maximum and minimum: max / min

qbpp::max(f, g) and qbpp::min(f, g) denote the maximum and minimum of two expressions. The following program assigns four jobs to two machines and minimizes the larger load (the makespan):

#include <qbpp/exhaustive_solver.hpp>
#include <qbpp/qbpp.hpp>

int main() {
  auto a = qbpp::var("a", 4);
  int p[] = {5, 3, 7, 4};
  qbpp::Expr load1, load2;
  for (int i = 0; i < 4; ++i) {
    load1 += p[i] * a(i);
    load2 += p[i] * (1 - a(i));
  }
  auto f = qbpp::max(load1, load2);
  f.simplify_as_binary();
  auto solver = qbpp::ExhaustiveSolver(f);
  auto sol = solver.search();
  std::cout << "makespan = " << sol.energy() << std::endl;
}

The output of the program is as follows:

makespan = 10

Support and limitations

  • The bundled solvers (EasySolver, ABS3 Solver, and Exhaustive Solver) support abs and relu (exponents 1 and 2) as well as max and min.
  • GurobiSolver and ScipSolver (Quadratic formulation) support abs and relu with exponents 1 and 2. Linearization-based MIP solvers (SCIP Linearize, HiGHS, CBC, and GLPK) support exponent 1 only.
  • max and min expand to exact relu identities, so MIP solvers handle the convex directions — minimizing $w \cdot \max$ and maximizing min ($-w \cdot \min$ inside a minimized objective); the non-convex directions raise an explicit error.
  • Any constant coefficient works on the bundled solvers, including negative ones (a negative coefficient rewards a large function value). MIP solvers accept positive coefficients only. Expressions containing nonlinear functions do not support expand_cons() or reduce().
  • The value f(sol) of the expression at a solution sol always matches the energy reported by the solvers.

Relation to cons()

qbpp::cons() of Native Constraints is, value-wise, a member of the same family of nonlinear functions. An equality constraint has the same value as

\[\operatorname{cons}(f = k) = \operatorname{abs}(f - k,\, 2)\]

and a range constraint has the same value as

\[\operatorname{cons}(l \le f \le u) = \operatorname{relu}(l - f,\, 2) + \operatorname{relu}(f - u,\, 2)\]

(at most one side can be violated, so the two relu terms are never positive at the same time).

The difference is the semantics. cons() declares the expression as a constraint: it participates in the violation count (Viol) and in the feasibility / target_energy decisions. abs() and relu() are pure objective terms with no constraint semantics. Use cons() for conditions that must be satisfied, and abs() / relu() for quantities whose value itself is a cost.


Back to top

Page last modified: 2026.08.06.

© 2026 Koji Nakano, Hiroshima University