Skip to content

Commit

Permalink
Support norm1 atom (#35)
Browse files Browse the repository at this point in the history
Gurobi only handles vector norms, so we have to ensure we always use 1D
arrays when forwarding to `numpy` and/or `cvxpy` since they also have
matrix norms.

Part of #23
  • Loading branch information
jonathanberthias authored Aug 29, 2024
1 parent 1a5b207 commit 89da4e4
Show file tree
Hide file tree
Showing 20 changed files with 675 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/cvxpy_gurobi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,15 @@ def visit_multiply(self, mul: multiply) -> Any:
def visit_NegExpression(self, node: NegExpression) -> Any:
return -self.visit(node.args[0])

def visit_norm1(self, node: cp.norm1) -> Any:
(x,) = node.args
if isinstance(x, cp.Constant):
return np.linalg.norm(x.value.ravel(), 1)
arg = self.translate_into_variable(x)
varargs = [arg] if isinstance(arg, gp.Var) else arg.reshape(-1).tolist()
norm = gp.norm(varargs, 1)
return self.make_auxilliary_variable_for(norm, "norm1", lb=0)

def visit_power(self, node: power) -> Any:
power = self.visit(node.p)
if power != 2:
Expand Down
28 changes: 28 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_0__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CVXPY
Minimize
norm1(x)
Subject To
Bounds
x free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0
Subject To
R0: - C0 + C1 <= 0
R1: - C0 - C1 <= 0
Bounds
C0 free
C1 free
End
----------------------------------------
GUROBI
Minimize
0 x + norm1_1
Subject To
Bounds
x free
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( x )
End
34 changes: 34 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_10__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
CVXPY
Minimize
norm1(x) + norm1([ 1. -2.])
Subject To
Bounds
x free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0 + C1
Subject To
R0: - C0 + C2 <= 0
R1: - C1 + C3 <= 0
R2: - C0 - C2 <= 0
R3: - C1 - C3 <= 0
Bounds
C0 free
C1 free
C2 free
C3 free
End
----------------------------------------
GUROBI
Minimize
0 x[0] + 0 x[1] + norm1_1 + 3 Constant
Subject To
Bounds
x[0] free
x[1] free
Constant = 1
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( x[0] , x[1] )
End
36 changes: 36 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_11__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CVXPY
Maximize
Sum(x @ [ 1. -2.], None, False)
Subject To
40: norm1(x) <= 1.0
Bounds
x free
End
----------------------------------------
AFTER COMPILATION
Minimize
- C0 + 2 C1
Subject To
R0: C0 - C2 <= 0
R1: C1 - C3 <= 0
R2: - C0 - C2 <= 0
R3: - C1 - C3 <= 0
R4: C2 + C3 <= 1
Bounds
C0 free
C1 free
C2 free
C3 free
End
----------------------------------------
GUROBI
Maximize
x[0] - 2 x[1]
Subject To
40: norm1_1 <= 1
Bounds
x[0] free
x[1] free
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( x[0] , x[1] )
End
43 changes: 43 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_12__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
CVXPY
Minimize
norm1(X)
Subject To
Bounds
X free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0 + C1 + C2 + C3
Subject To
R0: - C0 + C4 <= 0
R1: - C1 + C5 <= 0
R2: - C2 + C6 <= 0
R3: - C3 + C7 <= 0
R4: - C0 - C4 <= 0
R5: - C1 - C5 <= 0
R6: - C2 - C6 <= 0
R7: - C3 - C7 <= 0
Bounds
C0 free
C1 free
C2 free
C3 free
C4 free
C5 free
C6 free
C7 free
End
----------------------------------------
GUROBI
Minimize
0 X[0,0] + 0 X[0,1] + 0 X[1,0] + 0 X[1,1] + norm1_1
Subject To
Bounds
X[0,0] free
X[0,1] free
X[1,0] free
X[1,1] free
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( X[0,0] , X[0,1] , X[1,0] , X[1,1] )
End
53 changes: 53 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_13__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
CVXPY
Minimize
norm1(X + -[[1.00 -2.00]
[3.00 -4.00]])
Subject To
Bounds
X free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0 + C1 + C2 + C3
Subject To
R0: - C0 + C4 <= 1
R1: - C1 + C5 <= 3
R2: - C2 + C6 <= -2
R3: - C3 + C7 <= -4
R4: - C0 - C4 <= -1
R5: - C1 - C5 <= -3
R6: - C2 - C6 <= 2
R7: - C3 - C7 <= 4
Bounds
C0 free
C1 free
C2 free
C3 free
C4 free
C5 free
C6 free
C7 free
End
----------------------------------------
GUROBI
Minimize
norm1_2
Subject To
R0: - X[0,0] + AddExpression_1[0,0] = -1
R1: - X[0,1] + AddExpression_1[0,1] = 2
R2: - X[1,0] + AddExpression_1[1,0] = -3
R3: - X[1,1] + AddExpression_1[1,1] = 4
Bounds
X[0,0] free
X[0,1] free
X[1,0] free
X[1,1] free
AddExpression_1[0,0] free
AddExpression_1[0,1] free
AddExpression_1[1,0] free
AddExpression_1[1,1] free
General Constraints
GC0: norm1_2 = NORM ( 1 ) ( AddExpression_1[0,0] , AddExpression_1[0,1] ,
AddExpression_1[1,0] , AddExpression_1[1,1] )
End
45 changes: 45 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_14__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
CVXPY
Minimize
norm1(X) + norm1([[1.00 -2.00]
[3.00 -4.00]])
Subject To
Bounds
X free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0 + C1 + C2 + C3
Subject To
R0: - C0 + C4 <= 0
R1: - C1 + C5 <= 0
R2: - C2 + C6 <= 0
R3: - C3 + C7 <= 0
R4: - C0 - C4 <= 0
R5: - C1 - C5 <= 0
R6: - C2 - C6 <= 0
R7: - C3 - C7 <= 0
Bounds
C0 free
C1 free
C2 free
C3 free
C4 free
C5 free
C6 free
C7 free
End
----------------------------------------
GUROBI
Minimize
0 X[0,0] + 0 X[0,1] + 0 X[1,0] + 0 X[1,1] + norm1_1 + 10 Constant
Subject To
Bounds
X[0,0] free
X[0,1] free
X[1,0] free
X[1,1] free
Constant = 1
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( X[0,0] , X[0,1] , X[1,0] , X[1,1] )
End
47 changes: 47 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_15__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
CVXPY
Maximize
Sum(X @ [[1.00 -2.00]
[3.00 -4.00]], None, False)
Subject To
55: norm1(X) <= 1.0
Bounds
X free
End
----------------------------------------
AFTER COMPILATION
Minimize
- C0 - 3 C1 + 2 C2 + 4 C3
Subject To
R0: C0 - C4 <= 0
R1: C1 - C5 <= 0
R2: C2 - C6 <= 0
R3: C3 - C7 <= 0
R4: - C0 - C4 <= 0
R5: - C1 - C5 <= 0
R6: - C2 - C6 <= 0
R7: - C3 - C7 <= 0
R8: C4 + C5 + C6 + C7 <= 1
Bounds
C0 free
C1 free
C2 free
C3 free
C4 free
C5 free
C6 free
C7 free
End
----------------------------------------
GUROBI
Maximize
X[0,0] - 2 X[0,1] + 3 X[1,0] - 4 X[1,1]
Subject To
55: norm1_1 <= 1
Bounds
X[0,0] free
X[0,1] free
X[1,0] free
X[1,1] free
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( X[0,0] , X[0,1] , X[1,0] , X[1,1] )
End
30 changes: 30 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_1__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CVXPY
Minimize
norm1(x + -1.0)
Subject To
Bounds
x free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0
Subject To
R0: - C0 + C1 <= 1
R1: - C0 - C1 <= -1
Bounds
C0 free
C1 free
End
----------------------------------------
GUROBI
Minimize
norm1_2
Subject To
R0: - x + AddExpression_1 = -1
Bounds
x free
AddExpression_1 free
General Constraints
GC0: norm1_2 = NORM ( 1 ) ( AddExpression_1 )
End
29 changes: 29 additions & 0 deletions tests/snapshots/all__lp_genexpr_norm1_2__0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
CVXPY
Minimize
norm1(x) + norm1(-1.0)
Subject To
Bounds
x free
End
----------------------------------------
AFTER COMPILATION
Minimize
C0
Subject To
R0: - C0 + C1 <= 0
R1: - C0 - C1 <= 0
Bounds
C0 free
C1 free
End
----------------------------------------
GUROBI
Minimize
0 x + norm1_1 + Constant
Subject To
Bounds
x free
Constant = 1
General Constraints
GC0: norm1_1 = NORM ( 1 ) ( x )
End
Loading

0 comments on commit 89da4e4

Please sign in to comment.