diff --git a/src/hull.jl b/src/hull.jl index 77741b5..c26e61d 100644 --- a/src/hull.jl +++ b/src/hull.jl @@ -105,13 +105,13 @@ function _disaggregate_nl_expression(model::Model, c::Number, ::VariableRef, met end # variable in NonlinearExpr function _disaggregate_nl_expression(model::Model, vref::VariableRef, bvref::VariableRef, method::_Hull) + ϵ = method.value if is_binary(vref) || !haskey(method.disjunct_variables, (vref, bvref)) #keep any binary variables or nested disaggregated variables unchanged - return vref + dvref = vref else #replace with disaggregated form - ϵ = method.value dvref = method.disjunct_variables[vref, bvref] - return dvref / ((1-ϵ)*bvref+ϵ) end + return dvref / ((1-ϵ)*bvref+ϵ) end # affine expression in NonlinearExpr function _disaggregate_nl_expression(model::Model, aff::AffExpr, bvref::VariableRef, method::_Hull) @@ -123,7 +123,7 @@ function _disaggregate_nl_expression(model::Model, aff::AffExpr, bvref::Variable else #replace other vars with disaggregated form dvref = method.disjunct_variables[vref, bvref] end - new_expr += coeff * dvref / ((1-ϵ)*bvref+ϵ) + new_expr += coeff * dvref / ((1-ϵ)*bvref+ϵ) end return new_expr end diff --git a/test/constraints/hull.jl b/test/constraints/hull.jl index 756078a..1e151ef 100644 --- a/test/constraints/hull.jl +++ b/test/constraints/hull.jl @@ -77,6 +77,22 @@ function test_aggregate_variable() @test refcons[1].set == MOI.EqualTo(0.) end +function test_disaggregate_expression_var() + model = GDPModel() + @variable(model, 10 <= x <= 100) + @variable(model, z, Logical) + DP._reformulate_logical_variables(model) + bvrefs = DP._indicator_to_binary(model) + + vrefs = Set([x]) + method = DP._Hull(Hull(1e-3, Dict(x => (0., 100.))), vrefs) + DP._disaggregate_variables(model, z, vrefs, method) + + refexpr = DP._disaggregate_expression(model, x, bvrefs[z], method) + x_z = variable_by_name(model, "x_z") + @test refexpr == x_z +end + function test_disaggregate_expression_var_binary() model = GDPModel() @variable(model, x, Bin) @@ -93,7 +109,7 @@ function test_disaggregate_expression_var_binary() @test refexpr == x end -function test_disaggregate_expression_var() +function test_disaggregate_expression_affine() model = GDPModel() @variable(model, 10 <= x <= 100) @variable(model, z, Logical) @@ -104,26 +120,28 @@ function test_disaggregate_expression_var() method = DP._Hull(Hull(1e-3, Dict(x => (0., 100.))), vrefs) DP._disaggregate_variables(model, z, vrefs, method) - refexpr = DP._disaggregate_expression(model, x, bvrefs[z], method) + refexpr = DP._disaggregate_expression(model, 2x + 1, bvrefs[z], method) x_z = variable_by_name(model, "x_z") - @test refexpr == x_z + zbin = variable_by_name(model, "z") + @test refexpr == 2x_z + 1zbin end -function test_disaggregate_expression_affine() +function test_disaggregate_expression_affine_mip() model = GDPModel() @variable(model, 10 <= x <= 100) + @variable(model, y, Bin) @variable(model, z, Logical) DP._reformulate_logical_variables(model) bvrefs = DP._indicator_to_binary(model) - vrefs = Set([x]) - method = DP._Hull(Hull(1e-3, Dict(x => (0., 100.))), vrefs) + vrefs = Set([x, y]) + method = DP._Hull(Hull(1e-3, Dict(x => (0., 100.), y => (0., 1.))), vrefs) DP._disaggregate_variables(model, z, vrefs, method) - refexpr = DP._disaggregate_expression(model, 2x + 1, bvrefs[z], method) + refexpr = DP._disaggregate_expression(model, 2x + y + 1, bvrefs[z], method) x_z = variable_by_name(model, "x_z") zbin = variable_by_name(model, "z") - @test refexpr == 2x_z + 1zbin + @test refexpr == 2x_z + y + 1zbin end function test_disaggregate_expression_quadratic() @@ -176,7 +194,10 @@ function test_disaggregate_nl_expression_var_binary() DP._disaggregate_variables(model, z, vrefs, method) refexpr = DP._disaggregate_nl_expression(model, x, bvrefs[z], method) - @test refexpr == x + ϵ = method.value + @test refexpr.head == :/ + @test x in refexpr.args + @test (1-ϵ)*bvrefs[z]+ϵ in refexpr.args end function test_disaggregate_nl_expression_var() @@ -222,6 +243,33 @@ function test_disaggregate_nl_expression_aff() @test (1-ϵ)*zbin+ϵ in arg2.args end +function test_disaggregate_nl_expression_aff_mip() + model = GDPModel() + @variable(model, 10 <= x <= 100) + @variable(model, y, Bin) + @variable(model, z, Logical) + DP._reformulate_logical_variables(model) + bvrefs = DP._indicator_to_binary(model) + + vrefs = Set([x,y]) + method = DP._Hull(Hull(1e-3, Dict(x => (0., 100.), y => (0., 1.))), vrefs) + DP._disaggregate_variables(model, z, vrefs, method) + + refexpr = DP._disaggregate_nl_expression(model, 2x + y + 1, bvrefs[z], method) + flatten!(refexpr) + x_z = variable_by_name(model, "x_z") + zbin = variable_by_name(model, "z") + ϵ = method.value + @test refexpr.head == :+ + @test 1 in refexpr.args + args2 = setdiff(refexpr.args, [1]) + for arg in args2 + @test arg.head == :/ + @test 2x_z in arg.args || 1y in arg.args + @test (1-ϵ)*zbin+ϵ in arg.args + end +end + function test_disaggregate_nl_expression_quad() model = GDPModel() @variable(model, 10 <= x <= 100) @@ -596,14 +644,16 @@ end test_query_variable_bounds_error2() test_disaggregate_variables() test_aggregate_variable() - test_disaggregate_expression_var_binary() test_disaggregate_expression_var() + test_disaggregate_expression_var_binary() test_disaggregate_expression_affine() + test_disaggregate_expression_affine_mip() test_disaggregate_expression_quadratic() test_disaggregate_nl_expression_c() - test_disaggregate_nl_expression_var_binary() test_disaggregate_nl_expression_var() + test_disaggregate_nl_expression_var_binary() test_disaggregate_nl_expression_aff() + test_disaggregate_nl_expression_aff_mip() test_disaggregate_nl_expression_quad() test_disaggregate_nl_expession() for s in (MOI.LessThan, MOI.GreaterThan, MOI.EqualTo)