Skip to content

Commit

Permalink
Add drop_zeros function (#1934)
Browse files Browse the repository at this point in the history
* Add drop_zeros function

* Fix tests

* drop_zeros -> drop_zeros!
  • Loading branch information
odow authored and mlubin committed May 12, 2019
1 parent d565641 commit 1845f50
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 15 deletions.
45 changes: 43 additions & 2 deletions docs/src/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ add_to_expression!(ex, 1.0, y)
2 x + y - 1
```

### Removing zero terms

Use [`drop_zeros!`](@ref) to remove terms from an affine expression with a `0`
coefficient.

```jldoctest
julia> model = Model();
julia> @variable(model, x)
x
julia> @expression(model, ex, 0 * x + 1)
0 x + 1
julia> drop_zeros!(ex)
julia> ex
1
```

## Quadratic expressions

Like affine expressions, there are four ways of constructing a quadratic
Expand Down Expand Up @@ -188,17 +208,38 @@ add_to_expression!(ex, 1.0, y, y)
x² + 2 x*y + y² + x + y - 1
```

### Removing zero terms

Use [`drop_zeros!`](@ref) to remove terms from a quadratic expression with a `0`
coefficient.

```jldoctest
julia> model = Model();
julia> @variable(model, x)
x
julia> @expression(model, ex, 0 * x^2 + x + 1)
0 x² + x + 1
julia> drop_zeros!(ex)
julia> ex
x + 1
```

## Nonlinear expressions

Nonlinear expressions can be constructed only using the [`@NLexpression`](@ref)
macro and can be used only in [`@NLobjective`](@ref), [`@NLconstraint`](@ref),
and other [`@NLexpression`](@ref)s. Moreover, quadratic and affine expressions
cannot be used in the nonlinear macros. For more details, see the [Nonlinear
Modeling](@ref) section.
Modeling](@ref) section.

## Reference

```@docs
@expression
JuMP.add_to_expression!
add_to_expression!
drop_zeros!
```
14 changes: 14 additions & 0 deletions src/aff_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ Base.one( a::GenericAffExpr) = one(typeof(a))
Base.copy(a::GenericAffExpr) = GenericAffExpr(copy(a.constant), copy(a.terms))
Base.broadcastable(a::GenericAffExpr) = Ref(a)

"""
drop_zeros!(expr::GenericAffExpr)
Remove terms in the affine expression with `0` coefficients.
"""
function drop_zeros!(expr::GenericAffExpr)
for (key, coef) in expr.terms
if iszero(coef)
delete!(expr.terms, key)
end
end
return
end

GenericAffExpr{C, V}() where {C, V} = zero(GenericAffExpr{C, V})

function map_coefficients_inplace!(f::Function, a::GenericAffExpr)
Expand Down
6 changes: 1 addition & 5 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,8 @@ function function_string(mode, a::GenericAffExpr, show_constant=true)

term_str = Array{String}(undef, 2 * length(linear_terms(a)))
elm = 1
# For each non-zero for this model
for (coef, var) in linear_terms(a)
_is_zero_for_printing(coef) && continue # e.g. x - x

for (coef, var) in linear_terms(a)
pre = _is_one_for_printing(coef) ? "" : _string_round(abs(coef)) * " "

term_str[2 * elm - 1] = _sign_string(coef)
Expand Down Expand Up @@ -339,8 +337,6 @@ function function_string(mode, q::GenericQuadExpr)
elm = 1
if length(term_str) > 0
for (coef, var1, var2) in quad_terms(q)
_is_zero_for_printing(coef) && continue # e.g. x - x

pre = _is_one_for_printing(coef) ? "" : _string_round(abs(coef)) * " "

x = function_string(mode, var1)
Expand Down
15 changes: 15 additions & 0 deletions src/quad_expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ Base.one(q::GenericQuadExpr) = one(typeof(q))
Base.copy(q::GenericQuadExpr) = GenericQuadExpr(copy(q.aff), copy(q.terms))
Base.broadcastable(q::GenericQuadExpr) = Ref(q)

"""
drop_zeros!(expr::GenericQuadExpr)
Remove terms in the quadratic expression with `0` coefficients.
"""
function drop_zeros!(expr::GenericQuadExpr)
drop_zeros!(expr.aff)
for (key, coef) in expr.terms
if iszero(coef)
delete!(expr.terms, key)
end
end
return
end

function map_coefficients_inplace!(f::Function, q::GenericQuadExpr)
# The iterator remains valid if existing elements are updated.
for (key, value) in q.terms
Expand Down
9 changes: 4 additions & 5 deletions test/operator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,16 @@ function operators_test(ModelType::Type{<:JuMP.AbstractModel}, VariableRefType::
@test_expression_with_string w + x "w + x"
@test_expression_with_string w - x "w - x"
@test_expression_with_string w * x "w*x"
@test_expression_with_string x - x "0"
@test_expression_with_string x - x "0 x"
@test_throws ErrorException w / x
@test_expression_with_string y*z - x "y*z - x"
@test_expression_with_string x - x "0"
# 2-3 Variable--AffExpr
@test_expression_with_string z + aff "z + 7.1 x + 2.5"
@test_expression_with_string z - aff "z - 7.1 x - 2.5"
@test_expression_with_string z * aff "7.1 z*x + 2.5 z"
@test_throws ErrorException z / aff
@test_throws MethodError z aff
@test_expression_with_string 7.1 * x - aff "-2.5"
@test_expression_with_string 7.1 * x - aff "0 x - 2.5"
# 2-4 Variable--QuadExpr
@test_expression_with_string w + q "2.5 y*z + w + 7.1 x + 2.5"
@test_expression_with_string w - q "-2.5 y*z + w - 7.1 x - 2.5"
Expand Down Expand Up @@ -189,14 +188,14 @@ function operators_test(ModelType::Type{<:JuMP.AbstractModel}, VariableRefType::
@test_expression_with_string aff - z "7.1 x - z + 2.5"
@test_expression_with_string aff * z "7.1 x*z + 2.5 z"
@test_throws ErrorException aff/z
@test_expression_with_string aff - 7.1 * x "2.5"
@test_expression_with_string aff - 7.1 * x "0 x + 2.5"
# 3-3 AffExpr--AffExpr
@test_expression_with_string aff + aff2 "7.1 x + 1.2 y + 3.7"
@test_expression_with_string aff - aff2 "7.1 x - 1.2 y + 1.3"
@test_expression_with_string aff * aff2 "8.52 x*y + 3 y + 8.52 x + 3"
@test string((x+x)*(x+3)) == string((x+3)*(x+x)) # Issue #288
@test_throws ErrorException aff/aff2
@test_expression_with_string aff-aff "0"
@test_expression_with_string aff-aff "0 x"
# 4-4 AffExpr--QuadExpr
@test_expression_with_string aff2 + q "2.5 y*z + 1.2 y + 7.1 x + 3.7"
@test_expression_with_string aff2 - q "-2.5 y*z + 1.2 y - 7.1 x - 1.3"
Expand Down
14 changes: 11 additions & 3 deletions test/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# test/print.jl
# Testing $fa pretty-printing-related functionality
#############################################################################
using MathOptInterface

using JuMP
using LinearAlgebra, Test
import JuMP.REPLMode, JuMP.IJuliaMode
Expand Down Expand Up @@ -116,6 +116,11 @@ end
ex = @expression(mod, -z*x[1] - x[1]*z + x[1]*x[2] + 0*z^2)
io_test(REPLMode, ex, "-2 x[1]*z + x[1]*x[2]")
io_test(IJuliaMode, ex, "-2 x_{1}\\times z + x_{1}\\times x_{2}")

ex = 0 * z^2 + 0 * x[1]
io_test(REPLMode, ex, "0 z² + 0 x[1]")
io_test(IJuliaMode, ex, "0 z$ijulia_sq + 0 x_{1}")

end

# See https://github.com/JuliaOpt/JuMP.jl/pull/1352
Expand All @@ -125,6 +130,9 @@ end
@variable m y
u = UnitNumber(2.0)
aff = JuMP.GenericAffExpr(zero(u), x => u, y => zero(u))
io_test(REPLMode, aff, "UnitNumber(2.0) x + UnitNumber(0.0) y")
io_test(IJuliaMode, aff, "UnitNumber(2.0) x + UnitNumber(0.0) y")
drop_zeros!(aff)
io_test(REPLMode, aff, "UnitNumber(2.0) x")
io_test(IJuliaMode, aff, "UnitNumber(2.0) x")
quad = aff * x
Expand Down Expand Up @@ -272,7 +280,7 @@ function printing_test(ModelType::Type{<:JuMP.AbstractModel})
model = ModelType()
@variable(model, x)
@variable(model, y)
zero_constr = @constraint(model, [x, y] in MathOptInterface.Zeros(2))
zero_constr = @constraint(model, [x, y] in MOI.Zeros(2))

io_test(REPLMode, zero_constr,
"[x, y] $in_sym MathOptInterface.Zeros(2)")
Expand Down Expand Up @@ -619,7 +627,7 @@ end
in_sym = JuMP._math_symbol(REPLMode, :in)
model = Model()
@variable(model, x >= 10)
zero_one = @constraint(model, x in MathOptInterface.ZeroOne())
zero_one = @constraint(model, x in MOI.ZeroOne())

io_test(REPLMode, JuMP.LowerBoundRef(x), "x $ge 10.0")
io_test(REPLMode, zero_one, "x binary")
Expand Down

0 comments on commit 1845f50

Please sign in to comment.