gc_pwl.py#

#!/usr/bin/env python3.11

# Copyright 2024, Gurobi Optimization, LLC

# This example formulates and solves the following simple model
# with PWL constraints:
#
#  maximize
#        sum c[j] * x[j]
#  subject to
#        sum A[i,j] * x[j] <= 0,  for i = 0, ..., m-1
#        sum y[j] <= 3
#        y[j] = pwl(x[j]),        for j = 0, ..., n-1
#        x[j] free, y[j] >= 0,    for j = 0, ..., n-1
#  where pwl(x) = 0,     if x  = 0
#               = 1+|x|, if x != 0
#
#  Note
#   1. sum pwl(x[j]) <= b is to bound x vector and also to favor sparse x vector.
#      Here b = 3 means that at most two x[j] can be nonzero and if two, then
#      sum x[j] <= 1
#   2. pwl(x) jumps from 1 to 0 and from 0 to 1, if x moves from negative 0 to 0,
#      then to positive 0, so we need three points at x = 0. x has infinite bounds
#      on both sides, the piece defined with two points (-1, 2) and (0, 1) can
#      extend x to -infinite. Overall we can use five points (-1, 2), (0, 1),
#      (0, 0), (0, 1) and (1, 2) to define y = pwl(x)
#

import gurobipy as gp
from gurobipy import GRB

try:
    n = 5
    m = 5
    c = [0.5, 0.8, 0.5, 0.1, -1]
    A = [
        [0, 0, 0, 1, -1],
        [0, 0, 1, 1, -1],
        [1, 1, 0, 0, -1],
        [1, 0, 1, 0, -1],
        [1, 0, 0, 1, -1],
    ]

    # Create a new model
    model = gp.Model("gc_pwl")

    # Create variables
    x = model.addVars(n, lb=-GRB.INFINITY, name="x")
    y = model.addVars(n, name="y")

    # Set objective
    model.setObjective(gp.quicksum(c[j] * x[j] for j in range(n)), GRB.MAXIMIZE)

    # Add Constraints
    for i in range(m):
        model.addConstr(gp.quicksum(A[i][j] * x[j] for j in range(n)) <= 0)

    model.addConstr(y.sum() <= 3)

    for j in range(n):
        model.addGenConstrPWL(x[j], y[j], [-1, 0, 0, 0, 1], [2, 1, 0, 1, 2])

    # Optimize model
    model.optimize()

    for j in range(n):
        print(f"{x[j].VarName} = {x[j].X:g}")

    print(f"Obj: {model.ObjVal:g}")

except gp.GurobiError as e:
    print(f"Error code {e.errno}: {e}")

except AttributeError:
    print("Encountered an attribute error")