diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 0c11249c37..2603c899ad 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -13,19 +13,23 @@ function. """ function eval_variables end -function eval_variables(varval::Function, f::SVF) +function eval_variables(varval::Function, f::MOI.SingleVariable) return varval(f.variable) end -function eval_variables(varval::Function, f::VVF) +function eval_variables(varval::Function, f::MOI.VectorOfVariables) return varval.(f.variables) end -function eval_variables(varval::Function, f::SAF) - return mapreduce(t -> eval_term(varval, t), +, f.terms, init = f.constant) +function eval_variables(varval::Function, f::MOI.ScalarAffineFunction) + out = f.constant + for t in f.terms + out += eval_term(varval, t) + end + return out end -function eval_variables(varval::Function, f::VAF) +function eval_variables(varval::Function, f::MOI.VectorAffineFunction) out = copy(f.constants) for t in f.terms out[t.output_index] += eval_term(varval, t.scalar_term) @@ -33,15 +37,18 @@ function eval_variables(varval::Function, f::VAF) return out end -function eval_variables(varval::Function, f::SQF) - 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 +function eval_variables(varval::Function, f::MOI.ScalarQuadraticFunction) + out = f.constant + for a in f.affine_terms + out += eval_term(varval, a) + end + for q in f.quadratic_terms + out += eval_term(varval, q) + end + return out end -function eval_variables(varval::Function, f::VQF) +function eval_variables(varval::Function, f::MOI.VectorQuadraticFunction) out = copy(f.constants) for t in f.affine_terms out[t.output_index] += eval_term(varval, t.scalar_term) @@ -52,11 +59,10 @@ function eval_variables(varval::Function, f::VQF) return out end -# Affine term function eval_term(varval::Function, t::MOI.ScalarAffineTerm) return t.coefficient * varval(t.variable) end -# Quadratic term + function eval_term(varval::Function, t::MOI.ScalarQuadraticTerm) tval = t.coefficient * varval(t.variable_1) * varval(t.variable_2) return t.variable_1 == t.variable_2 ? tval / 2 : tval @@ -103,8 +109,10 @@ const ObjectWithoutIndex = Union{ MOI.ModelLike, Symbol, } + const ObjectOrTupleWithoutIndex = Union{ObjectWithoutIndex,Tuple{Vararg{ObjectWithoutIndex}}} + const ObjectOrTupleOrArrayWithoutIndex = Union{ ObjectOrTupleWithoutIndex, AbstractArray{<:ObjectOrTupleWithoutIndex}, @@ -131,6 +139,7 @@ end map_indices(::F, block::MOI.NLPBlockData) where {F<:Function} = block # Terms + function map_indices(index_map::F, t::MOI.ScalarAffineTerm) where {F<:Function} return MOI.ScalarAffineTerm(t.coefficient, index_map(t.variable)) end @@ -146,8 +155,11 @@ function map_indices( index_map::F, t::MOI.ScalarQuadraticTerm, ) where {F<:Function} - inds = index_map.((t.variable_1, t.variable_2)) - return MOI.ScalarQuadraticTerm(t.coefficient, inds...) + return MOI.ScalarQuadraticTerm( + t.coefficient, + index_map(t.variable_1), + index_map(t.variable_2), + ) end function map_indices( @@ -170,20 +182,26 @@ 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::F, f::Union{SAF,VAF}) where {F<:Function} +function map_indices( + index_map::F, + f::Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}, +) where {F<:Function} return typeof(f)(map_indices.(index_map, f.terms), MOI.constant(f)) end -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)(quad, lin, MOI.constant(f)) +function map_indices( + index_map::F, + f::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, +) where {F<:Function} + affine_terms = map_indices.(index_map, f.affine_terms) + quadratic_terms = map_indices.(index_map, f.quadratic_terms) + return typeof(f)(quadratic_terms, affine_terms, MOI.constant(f)) end # Function changes function map_indices( - index_map::F, + ::F, change::Union{MOI.ScalarConstantChange,MOI.VectorConstantChange}, ) where {F<:Function} return change @@ -295,14 +313,10 @@ function substitute_variables( ) where {T,F<:Function} g = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], MOI.constant(func)) for term in func.terms - operate!( - +, - T, - g, - substitute_variables(variable_map, term), - )::typeof(func) + new_term = substitute_variables(variable_map, term) + operate!(+, T, g, new_term) end - return g + return g::MOI.ScalarAffineFunction{T} end function substitute_variables( @@ -315,9 +329,9 @@ function substitute_variables( ) for term in func.terms sub = substitute_variables(variable_map, term.scalar_term) - operate_output_index!(+, T, term.output_index, g, sub)::typeof(func) + operate_output_index!(+, T, term.output_index, g, sub) end - return g + return g::MOI.VectorAffineFunction{T} end function substitute_variables( @@ -329,21 +343,13 @@ function substitute_variables( MOI.ScalarAffineTerm{T}[], MOI.constant(func), ) - for term in func.affine_terms - operate!( - +, - T, - g, - substitute_variables(variable_map, term), - )::typeof(func) + for a_term in func.affine_terms + new_a_term = substitute_variables(variable_map, a_term) + operate!(+, T, g, new_a_term) end - for term in func.quadratic_terms - operate!( - +, - T, - g, - substitute_variables(variable_map, term), - )::typeof(func) + for q_term in func.quadratic_terms + new_q_term = substitute_variables(variable_map, q_term) + operate!(+, T, g, new_q_term) end return g end @@ -359,18 +365,28 @@ function substitute_variables( ) for term in func.affine_terms sub = substitute_variables(variable_map, term.scalar_term) - operate_output_index!(+, T, term.output_index, g, sub)::typeof(func) + operate_output_index!(+, T, term.output_index, g, sub) end for term in func.quadratic_terms sub = substitute_variables(variable_map, term.scalar_term) - operate_output_index!(+, T, term.output_index, g, sub)::typeof(func) + operate_output_index!(+, T, term.output_index, g, sub) end - return g + return g::MOI.VectorQuadraticFunction{T} end # Vector of constants -constant_vector(f::Union{SAF,SQF}) = [f.constant] -constant_vector(f::Union{VAF,VQF}) = f.constants + +function constant_vector( + f::Union{MOI.ScalarAffineFunction,MOI.ScalarQuadraticFunction}, +) + return [f.constant] +end + +function constant_vector( + f::Union{MOI.VectorAffineFunction,MOI.VectorQuadraticFunction}, +) + return f.constants +end # Implements iterator interface """ @@ -379,11 +395,12 @@ constant_vector(f::Union{VAF,VQF}) = f.constants Type of functions obtained by indexing objects obtained by calling `eachscalar` on functions of type `F`. """ -function scalar_type end scalar_type(::Type{MOI.VectorOfVariables}) = MOI.SingleVariable + function scalar_type(::Type{MOI.VectorAffineFunction{T}}) where {T} return MOI.ScalarAffineFunction{T} end + function scalar_type(::Type{MOI.VectorQuadraticFunction{T}}) where {T} return MOI.ScalarQuadraticFunction{T} end @@ -400,6 +417,7 @@ struct ScalarFunctionIterator{F<:MOI.AbstractVectorFunction,C} # an efficient implementation of `getindex`. cache::C end + function ScalarFunctionIterator(func::MOI.AbstractVectorFunction) return ScalarFunctionIterator(func, scalar_iterator_cache(func)) end @@ -434,18 +452,20 @@ struct ChainedIteratorAtIndex start::Int next::Vector{Int} end + function ChainedIteratorAtIndex(it::ChainedIterator, index::Int) return ChainedIteratorAtIndex(it.start[index], it.next) end + #TODO We could also precompute the length for each `output_index`, # check that it's a win. Base.IteratorSize(::ChainedIteratorAtIndex) = Base.SizeUnknown() + function Base.iterate(it::ChainedIteratorAtIndex, i = it.start) if iszero(i) return nothing - else - return i, it.next[i] end + return i, it.next[i] end function ScalarFunctionIterator(f::MOI.VectorAffineFunction) @@ -471,17 +491,30 @@ eachscalar(f::AbstractVector) = f function Base.iterate(it::ScalarFunctionIterator, state = 1) if state > length(it) return nothing - else - return (it[state], state + 1) end + return (it[state], state + 1) end function Base.length(it::ScalarFunctionIterator{<:MOI.AbstractVectorFunction}) return MOI.output_dimension(it.f) end -Base.eltype(it::ScalarFunctionIterator{VVF}) = SVF -Base.eltype(it::ScalarFunctionIterator{VAF{T}}) where {T} = SAF{T} -Base.eltype(it::ScalarFunctionIterator{VQF{T}}) where {T} = SQF{T} + +function Base.eltype(::ScalarFunctionIterator{MOI.VectorOfVariables}) + return MOI.SingleVariable +end + +function Base.eltype( + ::ScalarFunctionIterator{MOI.VectorAffineFunction{T}}, +) where {T} + return MOI.ScalarAffineFunction{T} +end + +function Base.eltype( + ::ScalarFunctionIterator{MOI.VectorQuadraticFunction{T}}, +) where {T} + return MOI.ScalarQuadraticFunction{T} +end + Base.lastindex(it::ScalarFunctionIterator) = length(it) # Define getindex for Vector functions @@ -607,7 +640,9 @@ end """ unsafe_add(t1::MOI.ScalarAffineTerm, t2::MOI.ScalarAffineTerm) -Sums the coefficients of `t1` and `t2` and returns an output `MOI.ScalarAffineTerm`. It is unsafe because it uses the `variable` of `t1` as the `variable` of the output without checking that it is equal to that of `t2`. +Sums the coefficients of `t1` and `t2` and returns an output +`MOI.ScalarAffineTerm`. It is unsafe because it uses the `variable` of `t1` as +the `variable` of the output without checking that it is equal to that of `t2`. """ function unsafe_add(t1::MOI.ScalarAffineTerm, t2::MOI.ScalarAffineTerm) return MOI.ScalarAffineTerm(t1.coefficient + t2.coefficient, t1.variable) @@ -632,14 +667,17 @@ end """ unsafe_add(t1::MOI.VectorAffineTerm, t2::MOI.VectorAffineTerm) -Sums the coefficients of `t1` and `t2` and returns an output `MOI.VectorAffineTerm`. It is unsafe because it uses the `output_index` and `variable` of `t1` as the `output_index` and `variable` of the output term without checking that they are equal to those of `t2`. +Sums the coefficients of `t1` and `t2` and returns an output +`MOI.VectorAffineTerm`. It is unsafe because it uses the `output_index` and +`variable` of `t1` as the `output_index` and `variable` of the output term +without checking that they are equal to those of `t2`. """ function unsafe_add( - t1::VT, - t2::VT, -) where {VT<:Union{MOI.VectorAffineTerm,MOI.VectorQuadraticTerm}} + t1::T, + t2::T, +) where {T<:Union{MOI.VectorAffineTerm,MOI.VectorQuadraticTerm}} scalar_term = unsafe_add(t1.scalar_term, t2.scalar_term) - return VT(t1.output_index, scalar_term) + return T(t1.output_index, scalar_term) end is_canonical(::Union{MOI.SingleVariable,MOI.VectorOfVariables}) = true @@ -650,7 +688,9 @@ is_canonical(::Union{MOI.SingleVariable,MOI.VectorOfVariables}) = true Returns a Bool indicating whether the function is in canonical form. See [`canonical`](@ref). """ -function is_canonical(f::Union{SAF,VAF}) +function is_canonical( + f::Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}, +) return is_strictly_sorted( f.terms, MOI.term_indices, @@ -664,13 +704,15 @@ end Returns a Bool indicating whether the function is in canonical form. See [`canonical`](@ref). """ -function is_canonical(f::Union{SQF,VQF}) +function is_canonical( + f::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, +) v = is_strictly_sorted( f.affine_terms, MOI.term_indices, t -> !iszero(MOI.coefficient(t)), ) - return v &= is_strictly_sorted( + return v & is_strictly_sorted( f.quadratic_terms, MOI.term_indices, t -> !iszero(MOI.coefficient(t)), @@ -707,21 +749,31 @@ function is_strictly_sorted(x::Vector, by, filter) end """ - canonical(f::Union{ScalarAffineFunction, VectorAffineFunction, - ScalarQuadraticFunction, VectorQuadraticFunction}) + canonical( + f::Union{ + ScalarAffineFunction, + VectorAffineFunction, + ScalarQuadraticFunction, + VectorQuadraticFunction, + }, + ) Returns the function in a canonical form, i.e. -* A term appear only once. -* The coefficients are nonzero. -* The terms appear in increasing order of variable where there the order of the variables is the order of their value. -* For a `AbstractVectorFunction`, the terms are sorted in ascending order of output index. + * A term appear only once. + * The coefficients are nonzero. + * The terms appear in increasing order of variable where there the order of the + variables is the order of their value. + * For a `AbstractVectorFunction`, the terms are sorted in ascending order of + output index. -The output of `canonical` can be assumed to be a copy of `f`, even for `VectorOfVariables`. +The output of `canonical` can be assumed to be a copy of `f`, even for +`VectorOfVariables`. -### Examples -If `x` (resp. `y`, `z`) is `VariableIndex(1)` (resp. 2, 3). -The canonical representation of `ScalarAffineFunction([y, x, z, x, z], [2, 1, 3, -2, -3], 5)` is `ScalarAffineFunction([x, y], [-1, 2], 5)`. +## Examples +If `x` (resp. `y`, `z`) is `VariableIndex(1)` (resp. 2, 3). The canonical +representation of `ScalarAffineFunction([y, x, z, x, z], [2, 1, 3, -2, -3], 5)` +is `ScalarAffineFunction([x, y], [-1, 2], 5)`. """ canonical(f::MOI.AbstractFunction) = canonicalize!(copy(f)) @@ -733,7 +785,9 @@ canonicalize!(f::Union{MOI.VectorOfVariables,MOI.SingleVariable}) = f Convert a function to canonical form in-place, without allocating a copy to hold the result. See [`canonical`](@ref). """ -function canonicalize!(f::Union{SAF,VAF}) +function canonicalize!( + f::Union{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}, +) sort_and_compress!( f.terms, MOI.term_indices, @@ -749,7 +803,9 @@ end Convert a function to canonical form in-place, without allocating a copy to hold the result. See [`canonical`](@ref). """ -function canonicalize!(f::Union{SQF,VQF}) +function canonicalize!( + f::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, +) sort_and_compress!( f.affine_terms, MOI.term_indices, @@ -766,12 +822,17 @@ function canonicalize!(f::Union{SQF,VQF}) end """ - sort_and_compress!(x::AbstractVector, by::Function, keep::Function, combine::Function) + sort_and_compress!( + x::AbstractVector, + by::Function, + keep::Function, + combine::Function, + ) -Sort the vector `x` in-place using `by` as the function from elements to comparable keys, then -combine all entries for which `by(x[i]) == by(x[j])` using the function `x[i] = combine(x[i], x[j])`, -and remove any entries for which `keep(x[i]) == false`. This may result in `x` being resized to -a shorter length. +Sort the vector `x` in-place using `by` as the function from elements to +comparable keys, then combine all entries for which `by(x[i]) == by(x[j])` using +the function `x[i] = combine(x[i], x[j])`, and remove any entries for which +`keep(x[i]) == false`. This may result in `x` being resized to a shorter length. """ function sort_and_compress!(x::AbstractVector, by, keep, combine) if length(x) > 0 @@ -890,17 +951,21 @@ then `isapprox_zero(f)` is `false` but `isapprox_zero(MOIU.canonical(f))` is function isapprox_zero end isapprox_zero(α::AbstractFloat, tol) = -tol <= α <= tol + isapprox_zero(α::Union{Integer,Rational}, tol) = iszero(α) + function isapprox_zero(f::MOI.AbstractFunction, tol) return all_coefficients(α -> isapprox_zero(α, tol), f) end _is_constant(f::MOI.ScalarAffineFunction) = isempty(f.terms) + function _is_constant(f::MOI.ScalarQuadraticFunction) return isempty(f.affine_terms) && isempty(f.quadratic_terms) end Base.iszero(::MOI.SingleVariable) = false + function Base.iszero( f::Union{MOI.ScalarAffineFunction,MOI.ScalarQuadraticFunction}, ) @@ -908,6 +973,7 @@ function Base.iszero( end Base.isone(::MOI.SingleVariable) = false + function Base.isone( f::Union{MOI.ScalarAffineFunction,MOI.ScalarQuadraticFunction}, ) @@ -1062,6 +1128,7 @@ Return a new function `f` with the variable vi removed. function remove_variable(f::MOI.AbstractFunction, vi::MOI.VariableIndex) return filter_variables(v -> v != vi, f) end + function remove_variable( f::MOI.AbstractFunction, vis::Vector{MOI.VariableIndex}, @@ -1076,17 +1143,39 @@ end Return a new function `f` modified according to `change`. """ -function modify_function(f::SAF, change::MOI.ScalarConstantChange) - return SAF(f.terms, change.new_constant) +function modify_function( + f::MOI.ScalarAffineFunction, + change::MOI.ScalarConstantChange, +) + return MOI.ScalarAffineFunction(f.terms, change.new_constant) end -function modify_function(f::VAF, change::MOI.VectorConstantChange) - return VAF(f.terms, change.new_constant) + +function modify_function( + f::MOI.VectorAffineFunction, + change::MOI.VectorConstantChange, +) + return MOI.VectorAffineFunction(f.terms, change.new_constant) end -function modify_function(f::SQF, change::MOI.ScalarConstantChange) - return SQF(f.quadratic_terms, f.affine_terms, change.new_constant) + +function modify_function( + f::MOI.ScalarQuadraticFunction, + change::MOI.ScalarConstantChange, +) + return MOI.ScalarQuadraticFunction( + f.quadratic_terms, + f.affine_terms, + change.new_constant, + ) end -function modify_function(f::VQF, change::MOI.VectorConstantChange) - return VQF(f.quadratic_terms, f.affine_terms, change.new_constant) +function modify_function( + f::MOI.VectorQuadraticFunction, + change::MOI.VectorConstantChange, +) + return MOI.VectorQuadraticFunction( + f.quadratic_terms, + f.affine_terms, + change.new_constant, + ) end function _modifycoefficient( @@ -1198,8 +1287,11 @@ end # Arithmetic """ - operate(op::Function, ::Type{T}, - args::Union{T, MOI.AbstractFunction}...)::MOI.AbstractFunction where T + operate( + op::Function, + ::Type{T}, + args::Union{T,MOI.AbstractFunction}..., + )::MOI.AbstractFunction where {T} Returns an `MOI.AbstractFunction` representing the function resulting from the operation `op(args...)` on functions of coefficient type `T`. No argument can be @@ -1217,8 +1309,11 @@ function operate( end """ - operate!(op::Function, ::Type{T}, - args::Union{T, MOI.AbstractFunction}...)::MOI.AbstractFunction where T + operate!( + op::Function, + ::Type{T}, + args::Union{T, MOI.AbstractFunction}..., + )::MOI.AbstractFunction where {T} Returns an `MOI.AbstractFunction` representing the function resulting from the operation `op(args...)` on functions of coefficient type `T`. The first argument @@ -1237,9 +1332,12 @@ end """ operate_output_index!( - op::Function, ::Type{T}, output_index::Integer, + op::Function, + ::Type{T}, + output_index::Integer, func::MOI.AbstractVectorFunction - args::Union{T, MOI.AbstractScalarFunction}...)::MOI.AbstractFunction where T + args::Union{T, MOI.AbstractScalarFunction}... + )::MOI.AbstractFunction where {T} Returns an `MOI.AbstractVectorFunction` where the function at `output_index` is the result of the operation `op` applied to the function at `output_index` @@ -1260,8 +1358,11 @@ function operate_output_index!( end """ - promote_operation(op::Function, ::Type{T}, - ArgsTypes::Type{<:Union{T, MOI.AbstractFunction}}...) where T + promote_operation( + op::Function, + ::Type{T}, + ArgsTypes::Type{<:Union{T, MOI.AbstractFunction}}..., + ) where {T} Returns the type of the `MOI.AbstractFunction` returned to the call `operate(op, T, args...)` where the types of the arguments `args` are @@ -1282,9 +1383,11 @@ function operate_term( ) return term end + function operate_term(::typeof(-), term::MOI.ScalarAffineTerm) return MOI.ScalarAffineTerm(-term.coefficient, term.variable) end + function operate_term(::typeof(-), term::MOI.ScalarQuadraticTerm) return MOI.ScalarQuadraticTerm( -term.coefficient, @@ -1292,12 +1395,14 @@ function operate_term(::typeof(-), term::MOI.ScalarQuadraticTerm) term.variable_2, ) end + function operate_term(::typeof(-), term::MOI.VectorAffineTerm) return MOI.VectorAffineTerm( term.output_index, operate_term(-, term.scalar_term), ) end + function operate_term(::typeof(-), term::MOI.VectorQuadraticTerm) return MOI.VectorQuadraticTerm( term.output_index, @@ -1308,6 +1413,7 @@ end function operate_term(::typeof(*), α::T, t::MOI.ScalarAffineTerm{T}) where {T} return MOI.ScalarAffineTerm(α * t.coefficient, t.variable) end + # `<:Number` is a workaround for https://github.com/jump-dev/MathOptInterface.jl/issues/980 function operate_term( ::typeof(*), @@ -1316,6 +1422,7 @@ function operate_term( ) where {T<:Number} return MOI.ScalarAffineTerm(t.coefficient * β, t.variable) end + function operate_term( ::typeof(*), α::T, @@ -1336,6 +1443,7 @@ function operate_term( t.variable_2, ) end + function operate_term( ::typeof(*), t::MOI.ScalarQuadraticTerm{T}, @@ -1347,6 +1455,7 @@ function operate_term( t.variable_2, ) end + function operate_term( ::typeof(*), α::T, @@ -1378,12 +1487,14 @@ function operate_term(::typeof(*), α::T, t::MOI.VectorAffineTerm{T}) where {T} operate_term(*, α, t.scalar_term), ) end + function operate_term(::typeof(*), t::MOI.VectorAffineTerm{T}, α::T) where {T} return MOI.VectorAffineTerm( t.output_index, operate_term(*, t.scalar_term, α), ) end + function operate_term( ::typeof(*), α::T, @@ -1394,6 +1505,7 @@ function operate_term( operate_term(*, α, t.scalar_term), ) end + function operate_term( ::typeof(*), t::MOI.VectorQuadraticTerm{T}, @@ -1404,6 +1516,7 @@ function operate_term( operate_term(*, t.scalar_term, α), ) end + function operate_term( ::typeof(*), t1::MOI.VectorAffineTerm, @@ -1419,6 +1532,7 @@ end function operate_term(::typeof(/), t::MOI.ScalarAffineTerm{T}, α::T) where {T} return MOI.ScalarAffineTerm(t.coefficient / α, t.variable) end + function operate_term( ::typeof(/), t::MOI.ScalarQuadraticTerm{T}, @@ -1430,12 +1544,14 @@ function operate_term( t.variable_2, ) end + function operate_term(::typeof(/), t::MOI.VectorAffineTerm{T}, α::T) where {T} return MOI.VectorAffineTerm( t.output_index, operate_term(/, t.scalar_term, α), ) end + function operate_term( ::typeof(/), t::MOI.VectorQuadraticTerm{T}, @@ -1461,12 +1577,14 @@ function operate_terms( ) return terms end + function operate_terms!( ::typeof(-), terms::Vector{<:Union{MOI.ScalarAffineTerm,MOI.ScalarQuadraticTerm}}, ) return map!(term -> operate_term(-, term), terms, terms) end + function operate_terms( ::typeof(-), terms::Vector{ @@ -1480,6 +1598,7 @@ function operate_terms( ) return map(term -> operate_term(-, term), terms) end + function operate_terms( ::typeof(-), terms::Vector{<:Union{MOI.VectorAffineTerm,MOI.VectorQuadraticTerm}}, @@ -1493,6 +1612,7 @@ function map_terms!( ) return map!(op, func.terms, func.terms) end + function map_terms!( op, func::Union{MOI.ScalarQuadraticFunction,MOI.VectorQuadraticFunction}, @@ -1545,6 +1665,7 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function promote_operation( ::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1553,6 +1674,7 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function promote_operation( ::typeof(-), ::Type{T}, @@ -1560,6 +1682,7 @@ function promote_operation( ) where {T} return MOI.ScalarQuadraticFunction{T} end + function promote_operation( ::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1593,6 +1716,7 @@ function operate!( ) where {T} return MA.mutable_operate!(op, f, g) end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1601,6 +1725,7 @@ function operate!( ) where {T} return operate(op, T, f, g) end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1609,6 +1734,7 @@ function operate!( ) where {T} return operate(op, T, f, g) end + # Scalar Quadratic +/-! ... function operate!( op::Union{typeof(+),typeof(-)}, @@ -1627,12 +1753,12 @@ end ## operate # + with at least 3 arguments, can use in-place as the user cannot use # intermediate results -function operate(op::typeof(+), ::Type{T}, f, g, h, args...) where {T} +function operate(::typeof(+), ::Type{T}, f, g, h, args...) where {T} return operate!(+, T, operate(+, T, f, g), h, args...) end # Unary + -function operate(op::typeof(+), ::Type{T}, f::MOI.AbstractFunction) where {T} +function operate(::typeof(+), ::Type{T}, f::MOI.AbstractFunction) where {T} return f end @@ -1640,7 +1766,8 @@ end function operate(op::typeof(+), ::Type{T}, α::T, f::ScalarLike{T}) where {T} return operate(op, T, f, α) end -function operate(op::typeof(-), ::Type{T}, α::T, f::ScalarLike{T}) where {T} + +function operate(::typeof(-), ::Type{T}, α::T, f::ScalarLike{T}) where {T} return operate!(+, T, operate(-, T, f), α) end @@ -1651,6 +1778,7 @@ function operate(::typeof(-), ::Type{T}, f::MOI.SingleVariable) where {T} zero(T), ) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1662,6 +1790,7 @@ function operate( op(α), ) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1676,6 +1805,7 @@ function operate( zero(T), ) end + function operate( op::typeof(+), ::Type{T}, @@ -1684,14 +1814,16 @@ function operate( ) where {T} return operate(op, T, g, f) end + function operate( - op::typeof(-), + ::typeof(-), ::Type{T}, f::MOI.SingleVariable, g::Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}, ) where {T} return operate!(+, T, operate(-, T, g), f) end + # Scalar Affine +/- ... function operate( op::Union{typeof(-)}, @@ -1700,6 +1832,7 @@ function operate( ) where {T} return MOI.ScalarAffineFunction(operate_terms(op, f.terms), op(f.constant)) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1708,6 +1841,7 @@ function operate( ) where {T} return operate!(op, T, copy(f), g) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1720,6 +1854,7 @@ function operate( op(f.constant, g.constant), ) end + # Scalar Quadratic +/- ... function operate( op::Union{typeof(-)}, @@ -1732,6 +1867,7 @@ function operate( op(f.constant), ) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1745,6 +1881,7 @@ end function Base.:+(arg::ScalarLike{T}, args::ScalarLike{T}...) where {T} return operate(+, T, arg, args...) end + function Base.:+( α::T, arg::TypedScalarLike{T}, @@ -1752,33 +1889,42 @@ function Base.:+( ) where {T} return operate(+, T, α, arg, args...) end + function Base.:+(α::Number, f::MOI.SingleVariable) return operate(+, typeof(α), α, f) end + function Base.:+(f::TypedScalarLike{T}, α::T) where {T} return operate(+, T, f, α) end + function Base.:+(f::MOI.SingleVariable, α::Number) return operate(+, typeof(α), f, α) end + function Base.:-(arg::ScalarLike{T}, args::ScalarLike{T}...) where {T} return operate(-, T, arg, args...) end + function Base.:-(f::TypedScalarLike{T}, α::T) where {T} return operate(-, T, f, α) end + function Base.:-(f::MOI.SingleVariable, α::Number) return operate(-, typeof(α), f, α) end + function Base.:-(α::T, f::TypedScalarLike{T}) where {T} return operate(-, T, α, f) end + function Base.:-(α::Number, f::MOI.SingleVariable) return operate(-, typeof(α), α, f) end # Vector +/- ############################################################################### + function promote_operation( ::typeof(-), ::Type{T}, @@ -1786,6 +1932,7 @@ function promote_operation( ) where {T} return MOI.VectorAffineFunction{T} end + function promote_operation( ::typeof(-), ::Type{T}, @@ -1793,6 +1940,7 @@ function promote_operation( ) where {T} return MOI.VectorQuadraticFunction{T} end + function promote_operation( ::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1801,6 +1949,7 @@ function promote_operation( ) where {T} return MOI.VectorAffineFunction{T} end + function promote_operation( ::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1819,6 +1968,7 @@ function operate!( ) where {T} return operate(op, T, f, g) end + # Vector Affine +/-! ... function operate_output_index!( op::Union{typeof(+),typeof(-)}, @@ -1830,6 +1980,7 @@ function operate_output_index!( f.constants[output_index] = op(f.constants[output_index], α) return f end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1840,6 +1991,7 @@ function operate!( f.constants .= op.(f.constants, g) return f end + function operate_output_index!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1856,6 +2008,7 @@ function operate_output_index!( ) return f end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1867,12 +2020,13 @@ function operate!( append!( f.terms, MOI.VectorAffineTerm.( - collect(1:d), + 1:d, MOI.ScalarAffineTerm.(op(one(T)), g.variables), ), ) return f end + function operate_output_index!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1886,6 +2040,7 @@ function operate_output_index!( ) return operate_output_index!(op, T, output_index, f, MOI.constant(g)) end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1896,6 +2051,7 @@ function operate!( f.constants .= op.(f.constants, g.constants) return f end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1904,6 +2060,7 @@ function operate!( ) where {T} return operate(op, T, f, g) end + # Vector Quadratic +/-! ... function operate_output_index!( op::Union{typeof(+),typeof(-)}, @@ -1915,6 +2072,7 @@ function operate_output_index!( f.constants[output_index] = op(f.constants[output_index], α) return f end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1925,6 +2083,7 @@ function operate!( f.constants .= op.(f.constants, g) return f end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1942,6 +2101,7 @@ function operate!( ) return f end + function operate_output_index!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1955,6 +2115,7 @@ function operate_output_index!( ) return operate_output_index!(op, T, output_index, f, MOI.constant(g)) end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1965,6 +2126,7 @@ function operate!( f.constants .= op.(f.constants, g.constants) return f end + function operate_output_index!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -1985,6 +2147,7 @@ function operate_output_index!( ) return operate_output_index!(op, T, output_index, f, MOI.constant(g)) end + function operate!( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -2029,8 +2192,9 @@ function operate( ) where {T} return operate(op, T, f, α) end + function operate( - op::typeof(-), + ::typeof(-), ::Type{T}, α::Vector{T}, f::VectorLike{T}, @@ -2055,6 +2219,7 @@ function operate( op.(α), ) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -2066,17 +2231,18 @@ function operate( return MOI.VectorAffineFunction{T}( vcat( MOI.VectorAffineTerm.( - collect(1:d), + 1:d, MOI.ScalarAffineTerm.(one(T), f.variables), ), MOI.VectorAffineTerm.( - collect(1:d), + 1:d, MOI.ScalarAffineTerm.(op(one(T)), g.variables), ), ), fill(zero(T), d), ) end + function operate( op::typeof(+), ::Type{T}, @@ -2085,14 +2251,16 @@ function operate( ) where {T} return operate(op, T, g, f) end + function operate( - op::typeof(-), + ::typeof(-), ::Type{T}, f::MOI.VectorOfVariables, g::Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}, ) where {T} return operate!(+, T, operate(-, T, g), f) end + # Vector Affine +/- ... function operate( op::Union{typeof(-)}, @@ -2104,6 +2272,7 @@ function operate( op.(f.constants), ) end + function operate( op::Union{typeof(-)}, ::Type{T}, @@ -2115,6 +2284,7 @@ function operate( op.(f.constants), ) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -2123,6 +2293,7 @@ function operate( ) where {T} return operate!(op, T, copy(f), g) end + function operate( op::Union{typeof(+),typeof(-)}, ::Type{T}, @@ -2149,25 +2320,31 @@ end function Base.:+(args::VectorLike{T}...) where {T} return operate(+, T, args...) end + # Base.:+(α::Vector{T}, f::VectorLike{T}...) is too general as it also covers # Base.:+(α::Vector) which is type piracy function Base.:+(α::Vector{T}, f::VectorLike{T}, g::VectorLike{T}...) where {T} return operate(+, T, α, f, g...) end + function Base.:+(f::VectorLike{T}, α::Vector{T}) where {T} return operate(+, T, f, α) end + function Base.:-(args::VectorLike{T}...) where {T} return operate(-, T, args...) end + function Base.:-(f::VectorLike{T}, α::Vector{T}) where {T} return operate(-, T, f, α) end + function Base.:-(α::Vector{T}, f::VectorLike{T}) where {T} return operate(-, T, α, f) end ####################################### * ###################################### + function promote_operation( ::typeof(*), ::Type{T}, @@ -2176,6 +2353,7 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function promote_operation( ::typeof(*), ::Type{T}, @@ -2184,6 +2362,7 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function promote_operation( ::typeof(*), ::Type{T}, @@ -2192,6 +2371,7 @@ function promote_operation( ) where {T} return MOI.ScalarQuadraticFunction{T} end + function promote_operation( ::typeof(*), ::Type{T}, @@ -2200,6 +2380,7 @@ function promote_operation( ) where {T} return MOI.ScalarQuadraticFunction{T} end + function promote_operation( ::typeof(*), ::Type{T}, @@ -2212,12 +2393,14 @@ end function operate!(::typeof(*), ::Type{T}, f::MOI.SingleVariable, α::T) where {T} return operate(*, T, α, f) end + function operate(::typeof(*), ::Type{T}, α::T, f::MOI.SingleVariable) where {T} return MOI.ScalarAffineFunction{T}( [MOI.ScalarAffineTerm(α, f.variable)], zero(T), ) end + function operate( ::typeof(*), ::Type{T}, @@ -2232,6 +2415,7 @@ function operate( zeros(T, MOI.output_dimension(f)), ) end + function operate( ::typeof(*), ::Type{T}, @@ -2252,6 +2436,7 @@ function operate!( f.constant *= α return f end + function operate!( ::typeof(*), ::Type{T}, @@ -2259,9 +2444,10 @@ function operate!( α::T, ) where {T} map_terms!(term -> operate_term(*, term, α), f) - rmul!(f.constants, α) + LinearAlgebra.rmul!(f.constants, α) return f end + function operate(::typeof(*), ::Type{T}, α::T, f::TypedLike{T}) where {T} return operate!(*, T, copy(f), α) end @@ -2306,6 +2492,7 @@ function operate( ) return MOI.ScalarQuadraticFunction(quad_terms, aff_terms, zero(T)) end + function operate( ::typeof(*), ::Type{T}, @@ -2364,12 +2551,15 @@ function Base.:*( ) where {T} return operate(*, T, f, g, args...) end + function Base.:*(f::Number, g::Union{MOI.SingleVariable,MOI.VectorOfVariables}) return operate(*, typeof(f), f, g) end + function Base.:*(f::Union{MOI.SingleVariable,MOI.VectorOfVariables}, g::Number) return operate(*, typeof(g), f, g) end + # Used by sparse matrix multiplication after # https://github.com/JuliaLang/julia/pull/33205 function Base.:*(f::TypedLike, g::Bool) @@ -2379,6 +2569,7 @@ function Base.:*(f::TypedLike, g::Bool) return zero(typeof(f)) end end + Base.:*(f::Bool, g::TypedLike) = g * f function Base.:^(func::MOI.ScalarAffineFunction{T}, p::Integer) where {T} @@ -2392,6 +2583,7 @@ function Base.:^(func::MOI.ScalarAffineFunction{T}, p::Integer) where {T} throw(ArgumentError("Cannot take $(typeof(func)) to the power $p.")) end end + function Base.:^(func::MOI.ScalarQuadraticFunction{T}, p::Integer) where {T} if iszero(p) return one(MOI.ScalarQuadraticFunction{T}) @@ -2405,9 +2597,11 @@ end function LinearAlgebra.dot(f::ScalarLike, g::ScalarLike) return f * g end + function LinearAlgebra.dot(α::T, func::TypedLike{T}) where {T} return α * func end + function LinearAlgebra.dot(func::TypedLike{T}, α::T) where {T} return func * α end @@ -2418,6 +2612,7 @@ LinearAlgebra.symmetric_type(::Type{F}) where {F<:ScalarLike} = F LinearAlgebra.symmetric(f::ScalarLike, ::Symbol) = f ####################################### / ###################################### + function promote_operation( ::typeof(/), ::Type{T}, @@ -2426,6 +2621,7 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function promote_operation( ::typeof(/), ::Type{T}, @@ -2438,6 +2634,7 @@ end function operate!(::typeof(/), ::Type{T}, f::MOI.SingleVariable, α::T) where {T} return operate(/, T, f, α) end + function operate(::typeof(/), ::Type{T}, f::MOI.SingleVariable, α::T) where {T} return MOI.ScalarAffineFunction{T}( [MOI.ScalarAffineTerm(inv(α), f.variable)], @@ -2482,6 +2679,7 @@ end function operate(::typeof(/), ::Type{T}, f::TypedLike{T}, α::T) where {T} return operate!(/, T, copy(f), α) end + function operate( ::typeof(/), ::Type{T}, @@ -2494,14 +2692,21 @@ end function Base.:/(f::TypedLike{T}, g::T) where {T} return operate(/, T, f, g) end + function Base.:/(f::Union{MOI.SingleVariable,MOI.VectorOfVariables}, g::Number) return operate(/, typeof(g), f, g) end #################### Concatenation of MOI functions: `vcat` #################### + """ - fill_vector(vector::Vector, ::Type{T}, fill_func::Function, - dim_func::Function, funcs) where T + fill_vector( + vector::Vector, + ::Type{T}, + fill_func::Function, + dim_func::Function, + funcs, + ) where {T} Fill the vector `vector` with `fill_func(vector, vector_offset, output_offset, func)` for each function `func` @@ -2509,15 +2714,19 @@ in `funcs` where `vector_offset` (resp. `output_offset`) is the sum of `dim_func(T, func)` (resp. `output_dim(T, func)`) of previous functions of `func`. - fill_vector(vector::Vector, ::Type{T}, vector_offset::Int, - output_offset::Int, fill_func::Function, - dim_func::Function, funcs...) where T + fill_vector( + vector::Vector, + ::Type{T}, + vector_offset::Int, + output_offset::Int, + fill_func::Function, + dim_func::Function, + funcs... + ) where {T} Same than previous method but starting with possible nonzero `vector_offset` and `output_offset`. """ -function fill_vector end - function fill_vector( vector::Vector, ::Type{T}, @@ -2533,7 +2742,9 @@ function fill_vector( output_offset += output_dim(T, func) end @assert length(vector) == vector_offset + return end + function fill_vector( vector::Vector, ::Type, @@ -2543,7 +2754,9 @@ function fill_vector( dim_func::F2, ) where {F1<:Function,F2<:Function} @assert length(vector) == vector_offset + return end + function fill_vector( vector::Vector, ::Type{T}, @@ -2555,7 +2768,7 @@ function fill_vector( funcs..., ) where {T,F1<:Function,F2<:Function} fill_func(vector, vector_offset, output_offset, func) - return fill_vector( + fill_vector( vector, T, vector_offset + dim_func(T, func), @@ -2564,6 +2777,7 @@ function fill_vector( dim_func, funcs..., ) + return end function fill_variables( @@ -2572,16 +2786,18 @@ function fill_variables( output_offset::Int, func::MOI.SingleVariable, ) - return variables[offset+1] = func.variable + variables[offset+1] = func.variable + return end function fill_variables( variables::Vector{MOI.VariableIndex}, offset::Int, - output_offset::Int, + ::Int, func::MOI.VectorOfVariables, ) - return variables[offset.+(1:length(func.variables))] .= func.variables + variables[offset.+(1:length(func.variables))] .= func.variables + return end function promote_operation( @@ -2591,6 +2807,7 @@ function promote_operation( ) where {T} return MOI.VectorOfVariables end + function operate( ::typeof(vcat), ::Type{T}, @@ -2603,34 +2820,57 @@ function operate( end number_of_affine_terms(::Type{T}, ::T) where {T} = 0 -number_of_affine_terms(::Type, ::SVF) = 1 -number_of_affine_terms(::Type, f::VVF) = length(f.variables) -function number_of_affine_terms(::Type{T}, f::Union{SAF{T},VAF{T}}) where {T} + +number_of_affine_terms(::Type, ::MOI.SingleVariable) = 1 + +number_of_affine_terms(::Type, f::MOI.VectorOfVariables) = length(f.variables) + +function number_of_affine_terms( + ::Type{T}, + f::Union{MOI.ScalarAffineFunction{T},MOI.VectorAffineFunction{T}}, +) where {T} return length(f.terms) end -function number_of_affine_terms(::Type{T}, f::Union{SQF{T},VQF{T}}) where {T} + +function number_of_affine_terms( + ::Type{T}, + f::Union{MOI.ScalarQuadraticFunction{T},MOI.VectorQuadraticFunction{T}}, +) where {T} return length(f.affine_terms) end function number_of_quadratic_terms( ::Type{T}, - ::Union{T,SVF,VVF,SAF{T},VAF{T}}, + ::Union{ + T, + MOI.SingleVariable, + MOI.VectorOfVariables, + MOI.ScalarAffineFunction{T}, + MOI.VectorAffineFunction{T}, + }, ) where {T} return 0 end -function number_of_quadratic_terms(::Type{T}, f::Union{SQF{T},VQF{T}}) where {T} + +function number_of_quadratic_terms( + ::Type{T}, + f::Union{MOI.ScalarQuadraticFunction{T},MOI.VectorQuadraticFunction{T}}, +) where {T} return length(f.quadratic_terms) end function offset_term(t::MOI.ScalarAffineTerm, offset::Int) return MOI.VectorAffineTerm(offset + 1, t) end + function offset_term(t::MOI.VectorAffineTerm, offset::Int) return MOI.VectorAffineTerm(offset + t.output_index, t.scalar_term) end + function offset_term(t::MOI.ScalarQuadraticTerm, offset::Int) return MOI.VectorQuadraticTerm(offset + 1, t) end + function offset_term(t::MOI.VectorQuadraticTerm, offset::Int) return MOI.VectorQuadraticTerm(offset + t.output_index, t.scalar_term) end @@ -2640,98 +2880,126 @@ function fill_terms( ::Int, ::Int, ::T, -) where {T} end +) where {T} + return +end + function fill_terms( terms::Vector{MOI.VectorAffineTerm{T}}, offset::Int, output_offset::Int, - func::SVF, + func::MOI.SingleVariable, ) where {T} - return terms[offset+1] = + terms[offset+1] = offset_term(MOI.ScalarAffineTerm(one(T), func.variable), output_offset) + return end + function fill_terms( terms::Vector{MOI.VectorAffineTerm{T}}, offset::Int, output_offset::Int, - func::VVF, + func::MOI.VectorOfVariables, ) where {T} n = number_of_affine_terms(T, func) - return terms[offset.+(1:n)] .= + terms[offset.+(1:n)] .= MOI.VectorAffineTerm.( output_offset .+ (1:n), MOI.ScalarAffineTerm.(one(T), func.variables), ) + return end + function fill_terms( terms::Vector{MOI.VectorAffineTerm{T}}, offset::Int, output_offset::Int, - func::Union{SAF{T},VAF{T}}, + func::Union{MOI.ScalarAffineFunction{T},MOI.VectorAffineFunction{T}}, ) where {T} n = number_of_affine_terms(T, func) - return terms[offset.+(1:n)] .= offset_term.(func.terms, output_offset) + terms[offset.+(1:n)] .= offset_term.(func.terms, output_offset) + return end + function fill_terms( terms::Vector{MOI.VectorAffineTerm{T}}, offset::Int, output_offset::Int, - func::Union{SQF{T},VQF{T}}, + func::Union{MOI.ScalarQuadraticFunction{T},MOI.VectorQuadraticFunction{T}}, ) where {T} n = number_of_affine_terms(T, func) - return terms[offset.+(1:n)] .= - offset_term.(func.affine_terms, output_offset) + terms[offset.+(1:n)] .= offset_term.(func.affine_terms, output_offset) + return end function fill_terms( ::Vector{MOI.VectorQuadraticTerm{T}}, ::Int, ::Int, - ::Union{T,SVF,VVF,SAF{T},VAF{T}}, -) where {T} end + ::Union{ + T, + MOI.SingleVariable, + MOI.VectorOfVariables, + MOI.ScalarAffineFunction{T}, + MOI.VectorAffineFunction{T}, + }, +) where {T} + return +end + function fill_terms( terms::Vector{MOI.VectorQuadraticTerm{T}}, offset::Int, output_offset::Int, - func::Union{SQF{T},VQF{T}}, + func::Union{MOI.ScalarQuadraticFunction{T},MOI.VectorQuadraticFunction{T}}, ) where {T} n = number_of_quadratic_terms(T, func) - return terms[offset.+(1:n)] .= - offset_term.(func.quadratic_terms, output_offset) + terms[offset.+(1:n)] .= offset_term.(func.quadratic_terms, output_offset) + return end output_dim(::Type{T}, ::T) where {T} = 1 + output_dim(::Type, func::MOI.AbstractFunction) = MOI.output_dimension(func) + function fill_constant( constant::Vector{T}, offset::Int, - output_offset::Int, + ::Int, func::T, ) where {T} - return constant[offset+1] = func + constant[offset+1] = func + return end + function fill_constant( - constant::Vector{T}, - offset::Int, - output_offset::Int, - func::Union{SVF,VVF}, -) where {T} end + ::Vector{T}, + ::Int, + ::Int, + ::Union{MOI.SingleVariable,MOI.VectorOfVariables}, +) where {T} + return +end + function fill_constant( constant::Vector{T}, offset::Int, - output_offset::Int, - func::Union{SAF{T},SQF{T}}, + ::Int, + func::Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}, ) where {T} - return constant[offset+1] = func.constant + constant[offset+1] = func.constant + return end + function fill_constant( constant::Vector{T}, offset::Int, - output_offset::Int, - func::Union{VAF{T},VQF{T}}, + ::Int, + func::Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}, ) where {T} n = MOI.output_dimension(func) - return constant[offset.+(1:n)] .= func.constants + constant[offset.+(1:n)] .= func.constants + return end """ @@ -2799,25 +3067,44 @@ end function promote_operation(::typeof(vcat), ::Type{T}, ::Type{T}...) where {T} return Vector{T} end + function promote_operation( ::typeof(vcat), ::Type{T}, - ::Type{<:Union{ScalarAffineLike{T},VVF,VAF{T}}}..., + ::Type{ + <:Union{ + ScalarAffineLike{T}, + MOI.VectorOfVariables, + MOI.VectorAffineFunction{T}, + }, + }..., ) where {T} - return VAF{T} + return MOI.VectorAffineFunction{T} end + function promote_operation( ::typeof(vcat), ::Type{T}, - ::Type{<:Union{ScalarQuadraticLike{T},VVF,VAF{T},VQF{T}}}..., + ::Type{ + <:Union{ + ScalarQuadraticLike{T}, + MOI.VectorOfVariables, + MOI.VectorAffineFunction{T}, + MOI.VectorQuadraticFunction{T}, + }, + }..., ) where {T} - return VQF{T} + return MOI.VectorQuadraticFunction{T} end function operate( ::typeof(vcat), ::Type{T}, - funcs::Union{ScalarAffineLike{T},VVF,VAF{T}}..., + funcs::Union{ + ScalarAffineLike{T}, + MOI.VectorOfVariables, + MOI.VectorAffineFunction{T}, + }..., ) where {T} nterms = sum(func -> number_of_affine_terms(T, func), funcs) out_dim = sum(func -> output_dim(T, func), funcs) @@ -2825,12 +3112,18 @@ function operate( constant = zeros(T, out_dim) fill_vector(terms, T, 0, 0, fill_terms, number_of_affine_terms, funcs...) fill_vector(constant, T, 0, 0, fill_constant, output_dim, funcs...) - return VAF(terms, constant) + return MOI.VectorAffineFunction(terms, constant) end + function operate( ::typeof(vcat), ::Type{T}, - funcs::Union{ScalarQuadraticLike{T},VVF,VAF{T},VQF{T}}..., + funcs::Union{ + ScalarQuadraticLike{T}, + MOI.VectorOfVariables, + MOI.VectorAffineFunction{T}, + MOI.VectorQuadraticFunction{T}, + }..., ) where {T} num_affine_terms = sum(func -> number_of_affine_terms(T, func), funcs) num_quadratic_terms = sum(func -> number_of_quadratic_terms(T, func), funcs) @@ -2858,7 +3151,7 @@ function operate( funcs..., ) fill_vector(constant, T, 0, 0, fill_constant, output_dim, funcs...) - return VQF(quadratic_terms, affine_terms, constant) + return MOI.VectorQuadraticFunction(quadratic_terms, affine_terms, constant) end # Similar to `eachscalar` but faster, see @@ -2866,6 +3159,7 @@ end function scalarize(f::MOI.VectorOfVariables, ignore_constants::Bool = false) return MOI.SingleVariable.(f.variables) end + function scalarize( f::MOI.VectorAffineFunction{T}, ignore_constants::Bool = false, @@ -2884,6 +3178,7 @@ function scalarize( end return functions end + function scalarize( f::MOI.VectorQuadraticFunction{T}, ignore_constants::Bool = false, @@ -2916,8 +3211,9 @@ function count_terms(counting::Vector{<:Integer}, terms::Vector{T}) where {T} for term in terms counting[term.output_index] += 1 end - return nothing + return end + function count_terms(dimension::I, terms::Vector{T}) where {I,T} counting = zeros(I, dimension) count_terms(counting, terms) @@ -2925,8 +3221,11 @@ function count_terms(dimension::I, terms::Vector{T}) where {I,T} end tol_default(T::Type{<:Union{Integer,Rational}}) = zero(T) + tol_default(T::Type{<:AbstractFloat}) = sqrt(eps(T)) + convert_approx(::Type{T}, func::T; kws...) where {T} = func + function convert_approx( ::Type{MOI.SingleVariable}, func::MOI.ScalarAffineFunction{T}; @@ -2944,6 +3243,7 @@ function convert_approx( end return MOI.SingleVariable(f.terms[i].variable) end + function convert_approx( ::Type{MOI.VectorOfVariables}, func::MOI.VectorAffineFunction{T}; @@ -2958,6 +3258,7 @@ end function Base.zero(F::Type{<:TypedScalarLike{T}}) where {T} return convert(F, zero(T)) end + function Base.one(F::Type{<:TypedScalarLike{T}}) where {T} return convert(F, one(T)) end @@ -2967,6 +3268,7 @@ Base.promote_rule(::Type{F}, ::Type{T}) where {T,F<:TypedScalarLike{T}} = F function operate_coefficient(f, term::MOI.ScalarAffineTerm) return MOI.ScalarAffineTerm(f(term.coefficient), term.variable) end + function operate_coefficient(f, term::MOI.ScalarQuadraticTerm) return MOI.ScalarQuadraticTerm( f(term.coefficient), @@ -2974,12 +3276,14 @@ function operate_coefficient(f, term::MOI.ScalarQuadraticTerm) term.variable_2, ) end + function operate_coefficient(f, term::MOI.VectorAffineTerm) return MOI.VectorAffineTerm( term.output_index, operate_coefficient(f, term.scalar_term), ) end + function operate_coefficient(f, term::MOI.VectorQuadraticTerm) return MOI.VectorQuadraticTerm( term.output_index, @@ -2993,6 +3297,7 @@ function operate_coefficients(f, func::MOI.ScalarAffineFunction) f(func.constant), ) end + function operate_coefficients(f, func::MOI.ScalarQuadraticFunction) return MOI.ScalarQuadraticFunction( [operate_coefficient(f, term) for term in func.quadratic_terms], @@ -3000,12 +3305,14 @@ function operate_coefficients(f, func::MOI.ScalarQuadraticFunction) f(func.constant), ) end + function operate_coefficients(f, func::MOI.VectorAffineFunction) return MOI.VectorAffineFunction( [operate_coefficient(f, term) for term in func.terms], map(f, func.constants), ) end + function operate_coefficients(f, func::MOI.VectorQuadraticFunction) return MOI.VectorQuadraticFunction( [operate_coefficient(f, term) for term in func.quadratic_terms], @@ -3017,9 +3324,11 @@ end function Base.:*(α::T, g::TypedLike{T}) where {T} return operate_coefficients(β -> α * β, g) end + function Base.:*(α::Number, g::TypedLike) return operate_coefficients(β -> α * β, g) end + # Breaks ambiguity function Base.:*(α::T, g::TypedLike{T}) where {T<:Number} return operate_coefficients(β -> α * β, g) @@ -3028,9 +3337,11 @@ end function Base.:*(g::TypedLike{T}, β::T) where {T} return operate_coefficients(α -> α * β, g) end + function Base.:*(g::TypedLike, β::Number) return operate_coefficients(α -> α * β, g) end + # Breaks ambiguity function Base.:*(g::TypedLike{T}, β::T) where {T<:Number} return operate_coefficients(α -> α * β, g) @@ -3042,21 +3353,26 @@ function is_coefficient_type( ) return true end + is_coefficient_type(::Type{<:TypedLike{T}}, ::Type{T}) where {T} = true + is_coefficient_type(::Type{<:TypedLike}, ::Type) = false function similar_type(::Type{<:MOI.ScalarAffineFunction}, ::Type{T}) where {T} return MOI.ScalarAffineFunction{T} end + function similar_type( ::Type{<:MOI.ScalarQuadraticFunction}, ::Type{T}, ) where {T} return MOI.ScalarQuadraticFunction{T} end + function similar_type(::Type{<:MOI.VectorAffineFunction}, ::Type{T}) where {T} return MOI.VectorAffineFunction{T} end + function similar_type( ::Type{<:MOI.VectorQuadraticFunction}, ::Type{T}, @@ -3075,12 +3391,14 @@ function MA.promote_operation( end Base.real(f::TypedLike) = operate_coefficients(real, f) + function MA.promote_operation( ::typeof(real), T::Type{<:Union{MOI.SingleVariable,MOI.VectorOfVariables}}, ) return T end + Base.real(f::Union{MOI.SingleVariable,MOI.VectorOfVariables}) = f function promote_operation( @@ -3090,9 +3408,11 @@ function promote_operation( ) where {T} return MOI.ScalarAffineFunction{T} end + function operate(::typeof(imag), ::Type{T}, f::MOI.SingleVariable) where {T} return zero(MOI.ScalarAffineFunction{T}) end + function promote_operation( ::typeof(imag), ::Type{T}, @@ -3100,20 +3420,25 @@ function promote_operation( ) where {T} return MOI.VectorAffineFunction{T} end + function operate(::typeof(imag), ::Type{T}, f::MOI.VectorOfVariables) where {T} return zero_with_output_dimension( MOI.VectorAffineFunction{T}, MOI.output_dimension(f), ) end + Base.imag(f::TypedLike) = operate_coefficients(imag, f) + operate(::typeof(imag), ::Type, f::TypedLike) = imag(f) Base.conj(f::TypedLike) = operate_coefficients(conj, f) + function MA.promote_operation( ::typeof(conj), T::Type{<:Union{MOI.SingleVariable,MOI.VectorOfVariables}}, ) return T end + Base.conj(f::Union{MOI.SingleVariable,MOI.VectorOfVariables}) = f