From bf65f1e3bbba22426be20927962de9dc53509a63 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 5 Mar 2021 17:19:00 +1300 Subject: [PATCH 1/5] Force Julia to specialize on ::Function arguments. See the section in Performance Tips: https://docs.julialang.org/en/v1/manual/performance-tips/index.html#Be-aware-of-when-Julia-avoids-specializing --- src/Utilities/copy.jl | 32 ++--- src/Utilities/functions.jl | 237 ++++++++++++++++++++++++++----------- src/Utilities/model.jl | 26 ++-- src/Utilities/results.jl | 8 +- src/functions.jl | 6 +- 5 files changed, 210 insertions(+), 99 deletions(-) diff --git a/src/Utilities/copy.jl b/src/Utilities/copy.jl index c36f570065..03543113d9 100644 --- a/src/Utilities/copy.jl +++ b/src/Utilities/copy.jl @@ -2,7 +2,7 @@ """ automatic_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike; - copy_names::Bool=true, + copy_names::Bool=true, filter_constraints::Union{Nothing, Function}=nothing) Use [`Utilities.supports_default_copy_to`](@ref) and @@ -11,8 +11,8 @@ Use [`Utilities.supports_default_copy_to`](@ref) and apply the copy operation. If the `filter_constraints` arguments is given, only the constraints for which -this function returns `true` will be copied. This function is given a -constraint index as argument. +this function returns `true` will be copied. This function is given a +constraint index as argument. """ function automatic_copy_to( dest::MOI.ModelLike, @@ -402,8 +402,8 @@ Copy the constraints `cis_src` from the model `src` to the model `dest` and fill [`pass_attributes`] to copy the constraint attributes. If the `filter_constraints` arguments is given, only the constraints for which -this function returns `true` will be copied. This function is given a -constraint index as argument. +this function returns `true` will be copied. This function is given a +constraint index as argument. """ function copy_constraints( dest::MOI.ModelLike, @@ -443,7 +443,7 @@ function pass_constraints( filter_constraints::Union{Nothing,Function} = nothing, ) # copy_constraints can also take a filter_constraints argument; however, filtering - # is performed within this function (because it also calls MOI.set on the constraints). + # is performed within this function (because it also calls MOI.set on the constraints). # Don't pass this argument to copy_constraints/pass_cons to avoid a double filtering. for (S, cis_src) in zip(single_variable_types, single_variable_indices) if filter_constraints !== nothing @@ -497,8 +497,8 @@ function copy_free_variables( dest::MOI.ModelLike, idxmap::IndexMap, vis_src, - copy_variables::Function, -) + copy_variables::F, +) where {F<:Function} if length(vis_src) != length(keys(idxmap.varmap)) vars = copy_variables(dest, length(vis_src) - length(idxmap.varmap)) i = 1 @@ -549,9 +549,9 @@ function try_constrain_variables_on_creation( dest::MOI.ModelLike, src::MOI.ModelLike, idxmap, - copy_constrained_variables::Function, - copy_constrained_variable::Function, -) + copy_constrained_variables::F1, + copy_constrained_variable::F2, +) where {F1<:Function,F2<:Function} single_or_vector_variables_types = sorted_variable_sets_by_cost(dest, src) vector_of_variables_types = Type{<:MOI.AbstractVectorSet}[] vector_of_variables_added = @@ -605,8 +605,8 @@ constraints and attributes incrementally. The function the copying a model incrementally. If the `filter_constraints` arguments is given, only the constraints for which -this function returns `true` will be copied. This function is given a -constraint index as argument. +this function returns `true` will be copied. This function is given a +constraint index as argument. """ function default_copy_to( dest::MOI.ModelLike, @@ -972,7 +972,7 @@ function load_constraints( end """ - allocate_load(dest::MOI.ModelLike, src::MOI.ModelLike, + allocate_load(dest::MOI.ModelLike, src::MOI.ModelLike, filter_constraints::Union{Nothing, Function}=nothing ) @@ -981,8 +981,8 @@ Implements `MOI.copy_to(dest, src)` using the Allocate-Load API. The function the Allocate-Load API. If the `filter_constraints` arguments is given, only the constraints for which -this function returns `true` will be copied. This function is given a -constraint index as argument. +this function returns `true` will be copied. This function is given a +constraint index as argument. """ function allocate_load( dest::MOI.ModelLike, diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 8841388dc1..54129298dd 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -10,28 +10,42 @@ Returns the value of function `f` if each variable index `vi` is evaluated as `varval(vi)`. Note that `varval` should return a number, see [`substitute_variables`](@ref) for a similar function where `varval` returns a function. + +WARNING: Don't define `eval_variables(::Function, ...)` because Julia will +not specialize on this. Define instead +`eval_variables(::F, ...) where {F<:Function}`. """ function eval_variables end -eval_variables(varval::Function, f::SVF) = varval(f.variable) -eval_variables(varval::Function, f::VVF) = varval.(f.variables) -function eval_variables(varval::Function, f::SAF) + +function eval_variables(varval::F, f::SVF) where {F<:Function} + return varval(f.variable) +end + +function eval_variables(varval::F, f::VVF) where {F<:Function} + return varval.(f.variables) +end + +function eval_variables(varval::F, f::SAF) where {F<:Function} return mapreduce(t -> eval_term(varval, t), +, f.terms, init = f.constant) end -function eval_variables(varval::Function, f::VAF) + +function eval_variables(varval::F, f::VAF) where {F<:Function} out = copy(f.constants) for t in f.terms out[t.output_index] += eval_term(varval, t.scalar_term) end return out end -function eval_variables(varval::Function, f::SQF) + +function eval_variables(varval::F, f::SQF) where {F<:Function} init = zero(f.constant) lin = mapreduce(t -> eval_term(varval, t), +, f.affine_terms, init = init) quad = mapreduce(t -> eval_term(varval, t), +, f.quadratic_terms, init = init) return lin + quad + f.constant end -function eval_variables(varval::Function, f::VQF) + +function eval_variables(varval::F, f::VQF) where {F<:Function} out = copy(f.constants) for t in f.affine_terms out[t.output_index] += eval_term(varval, t.scalar_term) @@ -41,12 +55,13 @@ function eval_variables(varval::Function, f::VQF) end return out end + # Affine term -function eval_term(varval::Function, t::MOI.ScalarAffineTerm) +function eval_term(varval::F, t::MOI.ScalarAffineTerm) where {F<:Function} return t.coefficient * varval(t.variable_index) end # Quadratic term -function eval_term(varval::Function, t::MOI.ScalarQuadraticTerm) +function eval_term(varval::F, t::MOI.ScalarQuadraticTerm) where {F<:Function} tval = t.coefficient * varval(t.variable_index_1) * varval(t.variable_index_2) return t.variable_index_1 == t.variable_index_2 ? tval / 2 : tval @@ -95,31 +110,49 @@ const ObjectOrTupleOrArrayWithoutIndex = Union{ AbstractArray{<:AbstractArray{<:ObjectOrTupleWithoutIndex}}, } -map_indices(::Function, x::ObjectOrTupleOrArrayWithoutIndex) = x +map_indices(::F, x::ObjectOrTupleOrArrayWithoutIndex) where {F<:Function} = x + +function map_indices(index_map::F, vi::MOI.VariableIndex) where {F<:Function} + return index_map(vi) +end + +function map_indices(index_map::F, ci::MOI.ConstraintIndex) where {F<:Function} + return index_map(ci) +end -map_indices(index_map::Function, vi::MOI.VariableIndex) = index_map(vi) -map_indices(index_map::Function, ci::MOI.ConstraintIndex) = index_map(ci) -function map_indices(index_map::Function, array::AbstractArray{<:MOI.Index}) +function map_indices( + index_map::F, + array::AbstractArray{<:MOI.Index}, +) where {F<:Function} return map(index_map, array) end -map_indices(::Function, block::MOI.NLPBlockData) = block +map_indices(::F, block::MOI.NLPBlockData) where {F<:Function} = block # Terms -function map_indices(index_map::Function, t::MOI.ScalarAffineTerm) +function map_indices(index_map::F, t::MOI.ScalarAffineTerm) where {F<:Function} return MOI.ScalarAffineTerm(t.coefficient, index_map(t.variable_index)) end -function map_indices(index_map::Function, t::MOI.VectorAffineTerm) + +function map_indices(index_map::F, t::MOI.VectorAffineTerm) where {F<:Function} return MOI.VectorAffineTerm( t.output_index, map_indices(index_map, t.scalar_term), ) end -function map_indices(index_map::Function, t::MOI.ScalarQuadraticTerm) + +function map_indices( + index_map::F, + t::MOI.ScalarQuadraticTerm, +) where {F<:Function} inds = index_map.((t.variable_index_1, t.variable_index_2)) return MOI.ScalarQuadraticTerm(t.coefficient, inds...) end -function map_indices(index_map::Function, t::MOI.VectorQuadraticTerm) + +function map_indices( + index_map::F, + t::MOI.VectorQuadraticTerm, +) where {F<:Function} return MOI.VectorQuadraticTerm( t.output_index, map_indices(index_map, t.scalar_term), @@ -127,35 +160,48 @@ function map_indices(index_map::Function, t::MOI.VectorQuadraticTerm) end # Functions -function map_indices(index_map::Function, f::MOI.SingleVariable) + +function map_indices(index_map::F, f::MOI.SingleVariable) where {F<:Function} return MOI.SingleVariable(index_map(f.variable)) end -function map_indices(index_map::Function, f::MOI.VectorOfVariables) + +function map_indices(index_map::F, f::MOI.VectorOfVariables) where {F<:Function} return MOI.VectorOfVariables(index_map.(f.variables)) end -function map_indices(index_map::Function, f::Union{SAF,VAF}) + +function map_indices(index_map::F, f::Union{SAF,VAF}) where {F<:Function} return typeof(f)(map_indices.(index_map, f.terms), MOI.constant(f)) end -function map_indices(index_map::Function, f::Union{SQF,VQF}) + +function map_indices(index_map::F, f::Union{SQF,VQF}) where {F<:Function} lin = map_indices.(index_map, f.affine_terms) quad = map_indices.(index_map, f.quadratic_terms) return typeof(f)(lin, quad, MOI.constant(f)) end # Function changes + function map_indices( - index_map::Function, + index_map::F, change::Union{MOI.ScalarConstantChange,MOI.VectorConstantChange}, -) +) where {F<:Function} return change end -function map_indices(index_map::Function, change::MOI.ScalarCoefficientChange) + +function map_indices( + index_map::F, + change::MOI.ScalarCoefficientChange, +) where {F<:Function} return MOI.ScalarCoefficientChange( index_map(change.variable), change.new_coefficient, ) end -function map_indices(index_map::Function, change::MOI.MultirowChange) + +function map_indices( + index_map::F, + change::MOI.MultirowChange, +) where {F<:Function} return MOI.MultirowChange( index_map(change.variable), change.new_coefficients, @@ -176,20 +222,36 @@ This function is used by bridge optimizers on constraint functions, attribute values and submittable values when at least one variable bridge is used hence it needs to be implemented for custom types that are meant to be used as attribute or submittable value. + +WARNING: Don't use `substitude_variables(::Function, ...)` because Julia will +not specialize on this. Use instead +`substitude_variables(::F, ...) where {F<:Function}`. """ function substitute_variables end -substitute_variables(::Function, x::ObjectOrTupleOrArrayWithoutIndex) = x -substitute_variables(::Function, block::MOI.NLPBlockData) = block +function substitute_variables( + ::F, + x::ObjectOrTupleOrArrayWithoutIndex, +) where {F<:Function} + return x +end + +function substitute_variables(::F, block::MOI.NLPBlockData) where {F<:Function} + return block +end # Used when submitting `HeuristicSolution`. function substitute_variables( - variable_map::Function, + variable_map::F, vis::Vector{MOI.VariableIndex}, -) +) where {F<:Function} return substitute_variables.(variable_map, vis) end -function substitute_variables(variable_map::Function, vi::MOI.VariableIndex) + +function substitute_variables( + variable_map::F, + vi::MOI.VariableIndex, +) where {F<:Function} func = variable_map(vi) if func != MOI.SingleVariable(vi) error("Cannot substitute `$vi` as it is bridged into `$func`.") @@ -198,9 +260,9 @@ function substitute_variables(variable_map::Function, vi::MOI.VariableIndex) end function substitute_variables( - variable_map::Function, + variable_map::F, term::MOI.ScalarQuadraticTerm{T}, -) where {T} +) where {T,F<:Function} # We could have `T = Complex{Float64}` and `variable_map(term.variable_index)` # be a `MOI.ScalarAffineFunction{Float64}` with the Hermitian to PSD bridge. # We convert to `MOI.ScalarAffineFunction{T}` to avoid any issue. @@ -216,18 +278,20 @@ function substitute_variables( end return operate!(*, T, f12, coef) end + function substitute_variables( - variable_map::Function, + variable_map::F, term::MOI.ScalarAffineTerm{T}, -) where {T} +) where {T,F<:Function} # See comment for `term::MOI.ScalarQuadraticTerm` for the conversion. func::MOI.ScalarAffineFunction{T} = variable_map(term.variable_index) return operate(*, T, term.coefficient, func)::MOI.ScalarAffineFunction{T} end + function substitute_variables( - variable_map::Function, + variable_map::F, func::MOI.ScalarAffineFunction{T}, -) where {T} +) where {T,F<:Function} g = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], MOI.constant(func)) for term in func.terms operate!( @@ -239,10 +303,11 @@ function substitute_variables( end return g end + function substitute_variables( - variable_map::Function, + variable_map::F, func::MOI.VectorAffineFunction{T}, -) where {T} +) where {T,F<:Function} g = MOI.VectorAffineFunction( MOI.VectorAffineTerm{T}[], copy(MOI.constant(func)), @@ -253,10 +318,11 @@ function substitute_variables( end return g end + function substitute_variables( - variable_map::Function, + variable_map::F, func::MOI.ScalarQuadraticFunction{T}, -) where {T} +) where {T,F<:Function} g = MOI.ScalarQuadraticFunction( MOI.ScalarAffineTerm{T}[], MOI.ScalarQuadraticTerm{T}[], @@ -280,10 +346,11 @@ function substitute_variables( end return g end + function substitute_variables( - variable_map::Function, + variable_map::F, func::MOI.VectorQuadraticFunction{T}, -) where {T} +) where {T,F<:Function} g = MOI.VectorQuadraticFunction( MOI.VectorAffineTerm{T}[], MOI.VectorQuadraticTerm{T}[], @@ -760,13 +827,24 @@ end Determine whether predicate `p` returns `true` for all coefficients of `f`, returning `false` as soon as the first coefficient of `f` for which `p` returns `false` is encountered (short-circuiting). Similar to `all`. + +WARNING: Don't define `all_coefficients(::Function, ...)` because Julia will +not specialize on this. Define instead +`all_coefficients(::F, ...) where {F<:Function}`. """ function all_coefficients end -function all_coefficients(p::Function, f::MOI.ScalarAffineFunction) +function all_coefficients( + p::F, + f::MOI.ScalarAffineFunction, +) where {F<:Function} return p(f.constant) && all(t -> p(MOI.coefficient(t)), f.terms) end -function all_coefficients(p::Function, f::MOI.ScalarQuadraticFunction) + +function all_coefficients( + p::F, + f::MOI.ScalarQuadraticFunction, +) where {F<:Function} return p(f.constant) && all(t -> p(MOI.coefficient(t)), f.affine_terms) && all(t -> p(MOI.coefficient(t)), f.quadratic_terms) @@ -866,19 +944,28 @@ function test_models_equal( end end -_keep_all(keep::Function, v::MOI.VariableIndex) = keep(v) -_keep_all(keep::Function, t::MOI.ScalarAffineTerm) = keep(t.variable_index) -function _keep_all(keep::Function, t::MOI.ScalarQuadraticTerm) +_keep_all(keep::F, v::MOI.VariableIndex) where {F<:Function} = keep(v) + +function _keep_all(keep::F, t::MOI.ScalarAffineTerm) where {F<:Function} + return keep(t.variable_index) +end + +function _keep_all(keep::F, t::MOI.ScalarQuadraticTerm) where {F<:Function} return keep(t.variable_index_1) && keep(t.variable_index_2) end + function _keep_all( - keep::Function, + keep::F, t::Union{MOI.VectorAffineTerm,MOI.VectorQuadraticTerm}, -) +) where {F<:Function} return _keep_all(keep, t.scalar_term) end + # Removes terms or variables in `vis_or_terms` that contains the variable of index `vi` -function _filter_variables(keep::Function, variables_or_terms::Vector) +function _filter_variables( + keep::F, + variables_or_terms::Vector, +) where {F<:Function} return filter(el -> _keep_all(keep, el), variables_or_terms) end @@ -886,9 +973,14 @@ end filter_variables(keep::Function, f::AbstractFunction) Return a new function `f` with the variable `vi` such that `!keep(vi)` removed. + +WARNING: Don't define `filter_variables(::Function, ...)` because Julia will +not specialize on this. Define instead +`filter_variables(::F, ...) where {F<:Function}`. """ function filter_variables end -function filter_variables(keep::Function, f::MOI.SingleVariable) + +function filter_variables(keep::F, f::MOI.SingleVariable) where {F<:Function} if !keep(f.variable) error( "Cannot remove variable from a `SingleVariable` function of the", @@ -897,19 +989,22 @@ function filter_variables(keep::Function, f::MOI.SingleVariable) end return f end -function filter_variables(keep::Function, f::MOI.VectorOfVariables) + +function filter_variables(keep::F, f::MOI.VectorOfVariables) where {F<:Function} return MOI.VectorOfVariables(_filter_variables(keep, f.variables)) end + function filter_variables( - keep::Function, + keep::F, f::Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}, -) +) where {F<:Function} return typeof(f)(_filter_variables(keep, f.terms), MOI.constant(f)) end + function filter_variables( - keep, + keep::F, f::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, -) +) where {F<:Function} return typeof(f)( _filter_variables(keep, f.affine_terms), _filter_variables(keep, f.quadratic_terms), @@ -1075,10 +1170,10 @@ function operate end # Without `<:Number`, Julia v1.1.1 fails at precompilation with a StackOverflowError. function operate( - op::Function, + op::F, ::Type{T}, α::Union{T,AbstractVector{T}}..., -) where {T<:Number} +) where {T<:Number,F<:Function} return op(α...) end @@ -1094,10 +1189,10 @@ can be modified. The return type is the same than the method function operate! end function operate!( - op::Function, + op::F, ::Type{T}, α::Union{T,AbstractVector{T}}..., -) where {T} +) where {T,F<:Function} return op(α...) end @@ -1116,12 +1211,12 @@ argument can be modified. function operate_output_index! end function operate_output_index!( - op::Function, + op::F, ::Type{T}, i::Integer, x::Vector{T}, args..., -) where {T} +) where {T,F<:Function} return x[i] = operate!(op, T, x[i], args...) end @@ -2394,10 +2489,10 @@ function fill_vector end function fill_vector( vector::Vector, ::Type{T}, - fill_func::Function, - dim_func::Function, + fill_func::F1, + dim_func::F2, funcs, -) where {T} +) where {T,F1<:Function,F2<:Function} vector_offset = 0 output_offset = 0 for func in funcs @@ -2412,9 +2507,9 @@ function fill_vector( ::Type, vector_offset::Int, output_offset::Int, - fill_func::Function, - dim_func::Function, -) + fill_func::F1, + dim_func::F2, +) where {F1<:Function,F2<:Function} @assert length(vector) == vector_offset end function fill_vector( @@ -2422,11 +2517,11 @@ function fill_vector( ::Type{T}, vector_offset::Int, output_offset::Int, - fill_func::Function, - dim_func::Function, + fill_func::F1, + dim_func::F2, func, funcs..., -) where {T} +) where {T,F1<:Function,F2<:Function} fill_func(vector, vector_offset, output_offset, func) return fill_vector( vector, diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 4dde80e9e7..040b06750d 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -158,8 +158,16 @@ function _remove_variable(constrs::Vector{<:ConstraintEntry}, vi::VI) constrs[i] = (ci, remove_variable(f, s, vi)...) end end -filter_variables(keep::Function, f, s) = filter_variables(keep, f), s -function filter_variables(keep::Function, f::MOI.VectorOfVariables, s) + +function filter_variables(keep::F, f, s) where {F<:Function} + return filter_variables(keep, f), s +end + +function filter_variables( + keep::F, + f::MOI.VectorOfVariables, + s, +) where {F<:Function} g = filter_variables(keep, f) if length(g.variables) != length(f.variables) t = MOI.update_dimension(s, length(g.variables)) @@ -168,7 +176,11 @@ function filter_variables(keep::Function, f::MOI.VectorOfVariables, s) end return g, t end -function _filter_variables(keep::Function, constrs::Vector{<:ConstraintEntry}) + +function _filter_variables( + keep::F, + constrs::Vector{<:ConstraintEntry}, +) where {F<:Function} for i in eachindex(constrs) ci, f, s = constrs[i] constrs[i] = (ci, filter_variables(keep, f, s)...) @@ -1216,10 +1228,10 @@ macro model( end code = quote - function $MOIU.broadcastcall(f::Function, model::$esc_model_name) + function $MOIU.broadcastcall(f::F, model::$esc_model_name) where {F<:Function} return $(Expr(:block, _broadcastfield.(Ref(:(broadcastcall)), funs)...)) end - function $MOIU.broadcastvcat(f::Function, model::$esc_model_name) + function $MOIU.broadcastvcat(f::F, model::$esc_model_name) where {F<:Function} return vcat($(_broadcastfield.(Ref(:(broadcastvcat)), funs)...)) end function $MOI.empty!(model::$esc_model_name{T}) where {T} @@ -1245,10 +1257,10 @@ macro model( for (cname, sets) in ((scname, scalar_sets), (vcname, vector_sets)) code = quote $code - function $MOIU.broadcastcall(f::Function, model::$cname) + function $MOIU.broadcastcall(f::F, model::$cname) where {F<:Function} return $(Expr(:block, _callfield.(:f, sets)...)) end - function $MOIU.broadcastvcat(f::Function, model::$cname) + function $MOIU.broadcastvcat(f::F, model::$cname) where {F<:Function} return vcat($(_callfield.(:f, sets)...)) end function $MOI.empty!(model::$cname) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 2ea56d0d64..3afd095f2a 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -235,8 +235,8 @@ end function variable_coefficient( func::MOI.ScalarQuadraticFunction{T}, vi::MOI.VariableIndex, - value::Function, -) where {T} + value::F, +) where {T,F<:Function} coef = zero(T) # `vi`'th row of `Qx + a` where `func` is `x'Qx/2 + a'x + b`. for term in func.affine_terms @@ -256,8 +256,8 @@ end function variable_coefficient( func::MOI.VectorQuadraticFunction{T}, vi::MOI.VariableIndex, - value::Function, -) where {T} + value::F, +) where {T,F<:Function} coef = zeros(T, MOI.output_dimension(func)) # `vi`'th row of `Qx + a` where `func` is `x'Qx/2 + a'x + b`. for vector_term in func.affine_terms diff --git a/src/functions.jl b/src/functions.jl index c5cb64c440..90f5ef62fa 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -302,7 +302,11 @@ function Base.isapprox( end # For affine and quadratic functions, terms are compressed in a dictionary using `_dicts` and then the dictionaries are compared with `dict_compare` -function dict_compare(d1::Dict, d2::Dict{<:Any,T}, compare::Function) where {T} +function dict_compare( + d1::Dict, + d2::Dict{<:Any,T}, + compare::F, +) where {T,F<:Function} return all(kv -> compare(kv.second, Base.get(d2, kv.first, zero(T))), d1) end From d7e9ce9e2ce607a4ddbfc2675dbb6ec1eb2e5327 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Mar 2021 08:01:48 +1300 Subject: [PATCH 2/5] Undo some changes --- src/Utilities/functions.jl | 26 +++++++++++++------------- src/functions.jl | 6 +----- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 54129298dd..2784d5d1d9 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -944,28 +944,28 @@ function test_models_equal( end end -_keep_all(keep::F, v::MOI.VariableIndex) where {F<:Function} = keep(v) +_keep_all(keep::Function, v::MOI.VariableIndex) = keep(v) -function _keep_all(keep::F, t::MOI.ScalarAffineTerm) where {F<:Function} +function _keep_all(keep::Function, t::MOI.ScalarAffineTerm) return keep(t.variable_index) end -function _keep_all(keep::F, t::MOI.ScalarQuadraticTerm) where {F<:Function} +function _keep_all(keep::Function, t::MOI.ScalarQuadraticTerm) return keep(t.variable_index_1) && keep(t.variable_index_2) end function _keep_all( - keep::F, + keep::Function, t::Union{MOI.VectorAffineTerm,MOI.VectorQuadraticTerm}, -) where {F<:Function} +) return _keep_all(keep, t.scalar_term) end # Removes terms or variables in `vis_or_terms` that contains the variable of index `vi` function _filter_variables( - keep::F, + keep::Function, variables_or_terms::Vector, -) where {F<:Function} +) return filter(el -> _keep_all(keep, el), variables_or_terms) end @@ -1170,10 +1170,10 @@ function operate end # Without `<:Number`, Julia v1.1.1 fails at precompilation with a StackOverflowError. function operate( - op::F, + op::Function, ::Type{T}, α::Union{T,AbstractVector{T}}..., -) where {T<:Number,F<:Function} +) where {T<:Number} return op(α...) end @@ -1189,10 +1189,10 @@ can be modified. The return type is the same than the method function operate! end function operate!( - op::F, + op::Function, ::Type{T}, α::Union{T,AbstractVector{T}}..., -) where {T,F<:Function} +) where {T} return op(α...) end @@ -1211,12 +1211,12 @@ argument can be modified. function operate_output_index! end function operate_output_index!( - op::F, + op::Function, ::Type{T}, i::Integer, x::Vector{T}, args..., -) where {T,F<:Function} +) where {T} return x[i] = operate!(op, T, x[i], args...) end diff --git a/src/functions.jl b/src/functions.jl index 90f5ef62fa..c5cb64c440 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -302,11 +302,7 @@ function Base.isapprox( end # For affine and quadratic functions, terms are compressed in a dictionary using `_dicts` and then the dictionaries are compared with `dict_compare` -function dict_compare( - d1::Dict, - d2::Dict{<:Any,T}, - compare::F, -) where {T,F<:Function} +function dict_compare(d1::Dict, d2::Dict{<:Any,T}, compare::Function) where {T} return all(kv -> compare(kv.second, Base.get(d2, kv.first, zero(T))), d1) end From 79991597f0a6ce2cd2e1756b36e6648c81bade40 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Mar 2021 09:47:50 +1300 Subject: [PATCH 3/5] Undo eval_variables --- src/Utilities/functions.jl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 2784d5d1d9..04af76cc56 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -10,26 +10,22 @@ Returns the value of function `f` if each variable index `vi` is evaluated as `varval(vi)`. Note that `varval` should return a number, see [`substitute_variables`](@ref) for a similar function where `varval` returns a function. - -WARNING: Don't define `eval_variables(::Function, ...)` because Julia will -not specialize on this. Define instead -`eval_variables(::F, ...) where {F<:Function}`. """ function eval_variables end -function eval_variables(varval::F, f::SVF) where {F<:Function} +function eval_variables(varval::Function, f::SVF) return varval(f.variable) end -function eval_variables(varval::F, f::VVF) where {F<:Function} +function eval_variables(varval::Function, f::VVF) return varval.(f.variables) end -function eval_variables(varval::F, f::SAF) where {F<:Function} +function eval_variables(varval::Function, f::SAF) return mapreduce(t -> eval_term(varval, t), +, f.terms, init = f.constant) end -function eval_variables(varval::F, f::VAF) where {F<:Function} +function eval_variables(varval::Function, f::VAF) out = copy(f.constants) for t in f.terms out[t.output_index] += eval_term(varval, t.scalar_term) @@ -37,7 +33,7 @@ function eval_variables(varval::F, f::VAF) where {F<:Function} return out end -function eval_variables(varval::F, f::SQF) where {F<:Function} +function eval_variables(varval::Function, f::SQF) init = zero(f.constant) lin = mapreduce(t -> eval_term(varval, t), +, f.affine_terms, init = init) quad = @@ -45,7 +41,7 @@ function eval_variables(varval::F, f::SQF) where {F<:Function} return lin + quad + f.constant end -function eval_variables(varval::F, f::VQF) where {F<:Function} +function eval_variables(varval::Function, f::VQF) out = copy(f.constants) for t in f.affine_terms out[t.output_index] += eval_term(varval, t.scalar_term) @@ -57,11 +53,11 @@ function eval_variables(varval::F, f::VQF) where {F<:Function} end # Affine term -function eval_term(varval::F, t::MOI.ScalarAffineTerm) where {F<:Function} +function eval_term(varval::Function, t::MOI.ScalarAffineTerm) return t.coefficient * varval(t.variable_index) end # Quadratic term -function eval_term(varval::F, t::MOI.ScalarQuadraticTerm) where {F<:Function} +function eval_term(varval::Function, t::MOI.ScalarQuadraticTerm) tval = t.coefficient * varval(t.variable_index_1) * varval(t.variable_index_2) return t.variable_index_1 == t.variable_index_2 ? tval / 2 : tval From e0e0b839ec400093fc92edf55f2c6c3a8cf1c20f Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Mar 2021 09:50:51 +1300 Subject: [PATCH 4/5] Undo all_coefficients --- src/Utilities/functions.jl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 04af76cc56..1a044a5f1a 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -823,24 +823,14 @@ end Determine whether predicate `p` returns `true` for all coefficients of `f`, returning `false` as soon as the first coefficient of `f` for which `p` returns `false` is encountered (short-circuiting). Similar to `all`. - -WARNING: Don't define `all_coefficients(::Function, ...)` because Julia will -not specialize on this. Define instead -`all_coefficients(::F, ...) where {F<:Function}`. """ function all_coefficients end -function all_coefficients( - p::F, - f::MOI.ScalarAffineFunction, -) where {F<:Function} +function all_coefficients(p::Function, f::MOI.ScalarAffineFunction) return p(f.constant) && all(t -> p(MOI.coefficient(t)), f.terms) end -function all_coefficients( - p::F, - f::MOI.ScalarQuadraticFunction, -) where {F<:Function} +function all_coefficients(p::Function, f::MOI.ScalarQuadraticFunction) return p(f.constant) && all(t -> p(MOI.coefficient(t)), f.affine_terms) && all(t -> p(MOI.coefficient(t)), f.quadratic_terms) From a6c496c9e068a554801384d383ed9ac1b2853c12 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 8 Mar 2021 10:15:29 +1300 Subject: [PATCH 5/5] Undo filter_variables --- src/Utilities/functions.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 1a044a5f1a..b888600bce 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -966,7 +966,7 @@ not specialize on this. Define instead """ function filter_variables end -function filter_variables(keep::F, f::MOI.SingleVariable) where {F<:Function} +function filter_variables(keep::Function, f::MOI.SingleVariable) if !keep(f.variable) error( "Cannot remove variable from a `SingleVariable` function of the", @@ -976,21 +976,21 @@ function filter_variables(keep::F, f::MOI.SingleVariable) where {F<:Function} return f end -function filter_variables(keep::F, f::MOI.VectorOfVariables) where {F<:Function} +function filter_variables(keep::Function, f::MOI.VectorOfVariables) return MOI.VectorOfVariables(_filter_variables(keep, f.variables)) end function filter_variables( - keep::F, + keep::Function, f::Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}, -) where {F<:Function} +) return typeof(f)(_filter_variables(keep, f.terms), MOI.constant(f)) end function filter_variables( - keep::F, + keep::Function, f::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, -) where {F<:Function} +) return typeof(f)( _filter_variables(keep, f.affine_terms), _filter_variables(keep, f.quadratic_terms),