From cba0d8a29771304b570d263ada9c63c699955686 Mon Sep 17 00:00:00 2001 From: Twan Koolen Date: Mon, 11 Feb 2019 19:59:14 -0500 Subject: [PATCH 1/2] Add prune_zero option for canonicalize. --- src/functions.jl | 20 +++++++++++++++----- test/functions.jl | 4 ++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index f459370..18a94a2 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -249,8 +249,11 @@ function (f::AffineFunction{T})(vals::AbstractDict{Variable, S}) where {T, S} ret end -function canonicalize!(f::AffineFunction) +function canonicalize!(f::AffineFunction; prune_zero=false) sort_and_combine!(f.linear; by=term -> term.var.index, combine=combine, alg=Base.Sort.QuickSort) + if prune_zero + filter!(term -> !iszero(getcoeff(term)), f.linear) + end f end @@ -260,6 +263,8 @@ $(SIGNATURES) Return a canonicalized version of `f::AffineFunction`, namely with linear terms sorted by variable index and with terms corresponding to the same variable combined. +The `prune_zero` keyword argument (default: `false`) can be used to omit terms with zero coefficients. + # Example ```julia @@ -272,7 +277,7 @@ julia> canonicalize(f) 1 * x1 + -1 * x2 + 3 ``` """ -canonicalize(f::AffineFunction) = canonicalize!(AffineFunction(f)) +canonicalize(f::AffineFunction; prune_zero=false) = canonicalize!(AffineFunction(f); prune_zero=prune_zero) # QuadraticFunction @@ -354,10 +359,13 @@ function (f::QuadraticFunction{T})(vals::AbstractDict{Variable, S}) where {T, S} ret end -function canonicalize!(f::QuadraticFunction) - canonicalize!(f.affine) +function canonicalize!(f::QuadraticFunction; prune_zero=false) + canonicalize!(f.affine; prune_zero=prune_zero) canonicalized_variable_index_tuple = term -> (term = canonicalize(term); (term.rowvar.index, term.colvar.index)) sort_and_combine!(f.quadratic; by=canonicalized_variable_index_tuple, combine=combine, alg=Base.Sort.QuickSort) + if prune_zero + filter!(term -> !iszero(getcoeff(term)), f.quadratic) + end f end @@ -368,6 +376,8 @@ Return a canonicalized version of `f::QuadraticFunction`. See [`canonicalize(f:: and [`canonicalize(f::AffineFunction)`](@ref) for more details. Quadratic terms are ordered lexicographically by `(term.rowvar, term.colvar)`. +The `prune_zero` keyword argument (default: `false`) can be used to omit terms with zero coefficients. + # Example ```julia @@ -380,7 +390,7 @@ julia> canonicalize(f) 2 * x1 * x2 + 1 * x1 + 1 * x2 + 0 ``` """ -canonicalize(f::QuadraticFunction) = canonicalize!(QuadraticFunction(f)) +canonicalize(f::QuadraticFunction; prune_zero=false) = canonicalize!(QuadraticFunction(f); prune_zero=prune_zero) # copyto! diff --git a/test/functions.jl b/test/functions.jl index 146860d..93492c3 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -13,6 +13,10 @@ using Parametron.Functions @test canonicalize(3 * x * y) === canonicalize(3 * y * x) == QuadraticTerm(3, x, y) @test canonicalize(y + x - 2 * y + 3) == x - y + 3 @test canonicalize(x * y + y * x + y + y + x - y + 4) == 2 * x * y + x + y + 4 + @test canonicalize(0 * y + x + 1) == x + 0 * y + 1 + @test canonicalize(0 * y + x + 1; prune_zero=true) == x + 1 + @test canonicalize(0 * y^2 + x^2 + 0 * x + y + 1) == x^2 + 0 * y^2 + 0 * x + y + 1 + @test canonicalize(0 * y^2 + x^2 + 0 * x + y + 1; prune_zero=true) == x^2 + y + 1 f = x * y + y * x + y + y + x - y + 4 global allocs From 34095b3a284dcbb0921c723db4ea5320ca567e76 Mon Sep 17 00:00:00 2001 From: Twan Koolen Date: Mon, 11 Feb 2019 20:42:10 -0500 Subject: [PATCH 2/2] Separate prune_zero function. --- docs/src/api/functions.md | 5 ++++ src/functions.jl | 54 +++++++++++++++++++++++++++------------ test/functions.jl | 6 ++--- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/docs/src/api/functions.md b/docs/src/api/functions.md index c454f86..4fc2afa 100644 --- a/docs/src/api/functions.md +++ b/docs/src/api/functions.md @@ -28,6 +28,11 @@ canonicalize(::QuadraticFunction) canonicalize! ``` +```@docs +prune_zero +prune_zero! +``` + ## In-place math functions (unexported) ```@docs diff --git a/src/functions.jl b/src/functions.jl index 18a94a2..f47ef74 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -45,7 +45,9 @@ export export canonicalize, - canonicalize! + canonicalize!, + prune_zero, + prune_zero! using LinearAlgebra using DocStringExtensions @@ -69,6 +71,21 @@ In-place version of [`canonicalize`](@ref). """ function canonicalize! end +""" +$(METHODLIST) + +Prune terms with (approximately) zero coefficients. +""" +function prune_zero end + +""" +$(METHODLIST) + +In-place version of [`prune_zero`](@ref). +""" +function prune_zero! end + + # Variable """ @@ -249,11 +266,8 @@ function (f::AffineFunction{T})(vals::AbstractDict{Variable, S}) where {T, S} ret end -function canonicalize!(f::AffineFunction; prune_zero=false) +function canonicalize!(f::AffineFunction) sort_and_combine!(f.linear; by=term -> term.var.index, combine=combine, alg=Base.Sort.QuickSort) - if prune_zero - filter!(term -> !iszero(getcoeff(term)), f.linear) - end f end @@ -263,8 +277,6 @@ $(SIGNATURES) Return a canonicalized version of `f::AffineFunction`, namely with linear terms sorted by variable index and with terms corresponding to the same variable combined. -The `prune_zero` keyword argument (default: `false`) can be used to omit terms with zero coefficients. - # Example ```julia @@ -277,7 +289,14 @@ julia> canonicalize(f) 1 * x1 + -1 * x2 + 3 ``` """ -canonicalize(f::AffineFunction; prune_zero=false) = canonicalize!(AffineFunction(f); prune_zero=prune_zero) +canonicalize(f::AffineFunction) = canonicalize!(AffineFunction(f)) + +function prune_zero!(f::AffineFunction{T}; atol=zero(T)) where T + filter!(term -> abs(getcoeff(term)) > atol, f.linear) + f +end + +prune_zero(f::AffineFunction; kwargs...) = prune_zero!(AffineFunction(f); kwargs...) # QuadraticFunction @@ -359,13 +378,10 @@ function (f::QuadraticFunction{T})(vals::AbstractDict{Variable, S}) where {T, S} ret end -function canonicalize!(f::QuadraticFunction; prune_zero=false) - canonicalize!(f.affine; prune_zero=prune_zero) +function canonicalize!(f::QuadraticFunction) + canonicalize!(f.affine) canonicalized_variable_index_tuple = term -> (term = canonicalize(term); (term.rowvar.index, term.colvar.index)) sort_and_combine!(f.quadratic; by=canonicalized_variable_index_tuple, combine=combine, alg=Base.Sort.QuickSort) - if prune_zero - filter!(term -> !iszero(getcoeff(term)), f.quadratic) - end f end @@ -376,8 +392,6 @@ Return a canonicalized version of `f::QuadraticFunction`. See [`canonicalize(f:: and [`canonicalize(f::AffineFunction)`](@ref) for more details. Quadratic terms are ordered lexicographically by `(term.rowvar, term.colvar)`. -The `prune_zero` keyword argument (default: `false`) can be used to omit terms with zero coefficients. - # Example ```julia @@ -390,7 +404,15 @@ julia> canonicalize(f) 2 * x1 * x2 + 1 * x1 + 1 * x2 + 0 ``` """ -canonicalize(f::QuadraticFunction; prune_zero=false) = canonicalize!(QuadraticFunction(f); prune_zero=prune_zero) +canonicalize(f::QuadraticFunction) = canonicalize!(QuadraticFunction(f)) + +function prune_zero!(f::QuadraticFunction{T}; atol=zero(T)) where T + prune_zero!(f.affine) + filter!(term -> abs(getcoeff(term)) > atol, f.quadratic) + f +end + +prune_zero(f::QuadraticFunction; kwargs...) = prune_zero!(QuadraticFunction(f); kwargs...) # copyto! diff --git a/test/functions.jl b/test/functions.jl index 93492c3..44ca5c5 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -6,7 +6,7 @@ using StaticArrays: @SVector, SVector using Parametron using Parametron.Functions -@testset "canonicalize" begin +@testset "canonicalize, prune_zero" begin x = Variable(1) y = Variable(2) @@ -14,9 +14,9 @@ using Parametron.Functions @test canonicalize(y + x - 2 * y + 3) == x - y + 3 @test canonicalize(x * y + y * x + y + y + x - y + 4) == 2 * x * y + x + y + 4 @test canonicalize(0 * y + x + 1) == x + 0 * y + 1 - @test canonicalize(0 * y + x + 1; prune_zero=true) == x + 1 + @test prune_zero(canonicalize(0 * y + x + 1)) == x + 1 @test canonicalize(0 * y^2 + x^2 + 0 * x + y + 1) == x^2 + 0 * y^2 + 0 * x + y + 1 - @test canonicalize(0 * y^2 + x^2 + 0 * x + y + 1; prune_zero=true) == x^2 + y + 1 + @test prune_zero(canonicalize(0 * y^2 + x^2 + 0 * x + y + 1)) == x^2 + y + 1 f = x * y + y * x + y + y + x - y + 4 global allocs