Skip to content

Commit

Permalink
Add test
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Jun 6, 2024
1 parent 798e885 commit 4e5bee6
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
48 changes: 25 additions & 23 deletions src/models/ReLU.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ julia> y = Omelette.add_predictor(model, f, x)
julia> print(model)
Feasibility
Subject to
x[1] - _[5] + _[6] = 0
x[2] - _[7] + _[8] = 0
[_[5], _[6]] ∈ MathOptInterface.SOS1{Float64}([1.0, 2.0])
[_[7], _[8]] ∈ MathOptInterface.SOS1{Float64}([1.0, 2.0])
x[1] - omelette_y[1] + _[5] = 0
x[2] - omelette_y[2] + _[6] = 0
omelette_y[1] ≥ 0
omelette_y[2] ≥ 0
[omelette_y[1], _[5]] ∈ MathOptInterface.SOS1{Float64}([1.0, 2.0])
[omelette_y[2], _[6]] ∈ MathOptInterface.SOS1{Float64}([1.0, 2.0])
_[5] ≥ 0
_[6] ≥ 0
_[7] ≥ 0
_[8] ≥ 0
```
"""
struct ReLUSOS1 <: AbstractPredictor
Expand All @@ -118,9 +118,10 @@ function _add_predictor_inner(
y::Vector{JuMP.VariableRef},
)
for i in 1:length(x)
z = JuMP.@variable(model, [1:2], lower_bound = 0)
JuMP.@constraint(model, x[i] == z[1] - z[2])
JuMP.@constraint(model, z in MOI.SOS1([1.0, 2.0]))
z = JuMP.@variable(model, lower_bound = 0)
JuMP.@constraint(model, y[i] >= 0)
JuMP.@constraint(model, x[i] == y[i] - z)
JuMP.@constraint(model, [y[i], z] in MOI.SOS1([1.0, 2.0]))
end
return
end
Expand All @@ -131,8 +132,9 @@ end
Implements the ReLU constraint \$y = max(0, x)\$ by the reformulation:
```math
\\begin{aligned}
x = x^+ - x^- \\\\
x^+ \\times x^- = 0
x = y - z \\\\
y \\times z = 0 \\\\
y, z \\ge 0
\\end{aligned}
```
Expand All @@ -156,14 +158,14 @@ julia> y = Omelette.add_predictor(model, f, x)
julia> print(model)
Feasibility
Subject to
x[1] - _z[1]+ + _z[1]- = 0
x[2] - _z[2]+ + _z[2]- = 0
_z[1]+*_z[1]- = 0
_z[2]+*_z[2]- = 0
_z[1]+ ≥ 0
_z[1]- ≥ 0
_z[2]+ ≥ 0
_z[2]- ≥ 0
x[1] - omelette_y[1] + _z[1] = 0
x[2] - omelette_y[2] + _z[2] = 0
omelette_y[1] 0
omelette_y[2] 0
omelette_y[1]*_z[1] = 0
omelette_y[2]*_z[2] = 0
_z[1] ≥ 0
_z[2] ≥ 0
```
"""
struct ReLUQuadratic <: AbstractPredictor
Expand All @@ -179,10 +181,10 @@ function _add_predictor_inner(
y::Vector{JuMP.VariableRef},
)
for i in 1:length(x)
z_pos = JuMP.@variable(model, lower_bound = 0, base_name = "_z[$i]+")
z_neg = JuMP.@variable(model, lower_bound = 0, base_name = "_z[$i]-")
JuMP.@constraint(model, x[i] == z_pos - z_neg)
JuMP.@constraint(model, z_pos * z_neg == 0)
z = JuMP.@variable(model, lower_bound = 0, base_name = "_z[$i]")
JuMP.@constraint(model, y[i] >= 0)
JuMP.@constraint(model, x[i] == y[i] - z)
JuMP.@constraint(model, y[i] * z == 0)
end
return
end
23 changes: 18 additions & 5 deletions test/test_ReLU.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ module ReLUTests
using JuMP
using Test

import GLM
import HiGHS
import Ipopt
import Omelette

is_test(x) = startswith(string(x), "test_")
Expand All @@ -22,7 +22,8 @@ function runtests()
end

function test_ReLU_BigM()
model = Model()
model = Model(HiGHS.Optimizer)
set_silent(model)
@variable(model, x[1:2])
f = Omelette.ReLUBigM(2, 100.0)
@test size(f) == (2, 2)
Expand All @@ -31,6 +32,11 @@ function test_ReLU_BigM()
@test num_variables(model) == 6
@test num_constraints(model, AffExpr, MOI.LessThan{Float64}) == 4
@test num_constraints(model, AffExpr, MOI.GreaterThan{Float64}) == 4
@objective(model, Min, sum(y))
fix.(x, [-1, 2])
optimize!(model)
@assert is_solved_and_feasible(model)
@test value.(y) [0.0, 2.]
return
end

Expand All @@ -41,20 +47,27 @@ function test_ReLU_SOS1()
@test size(f) == (2, 2)
y = Omelette.add_predictor(model, f, x)
@test length(y) == 2
@test num_variables(model) == 8
@test num_variables(model) == 6
@test num_constraints(model, Vector{VariableRef}, MOI.SOS1{Float64}) == 2
# TODO(odow): add a test for solution with solver that supports SOS1
return
end

function test_ReLU_Quadratic()
model = Model()
model = Model(Ipopt.Optimizer)
set_silent(model)
@variable(model, x[1:2])
f = Omelette.ReLUQuadratic(2)
@test size(f) == (2, 2)
y = Omelette.add_predictor(model, f, x)
@test length(y) == 2
@test num_variables(model) == 8
@test num_variables(model) == 6
@test num_constraints(model, QuadExpr, MOI.EqualTo{Float64}) == 2
@objective(model, Min, sum(y))
fix.(x, [-1, 2])
optimize!(model)
@assert is_solved_and_feasible(model)
@test value.(y) [0.0, 2.]
return
end

Expand Down

0 comments on commit 4e5bee6

Please sign in to comment.