diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index 788dc19011..13360f618e 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -848,25 +848,30 @@ const ScalarAffineLike{T} = Union{T, MOI.SingleVariable, MOI.ScalarAffineFunctio # Functions convertible to a ScalarQuadraticFunction const ScalarQuadraticLike{T} = Union{ScalarAffineLike{T}, MOI.ScalarQuadraticFunction{T}} -# Used for overloading Base operator functions so `T` is not in the union to -# avoid overloading e.g. `+(::Float64, ::Float64)` -const ScalarLike{T} = Union{MOI.SingleVariable, MOI.ScalarAffineFunction{T}, - MOI.ScalarQuadraticFunction{T}} # `ScalarLike` for which `T` is defined to avoid defining, e.g., # `+(::SingleVariable, ::Any)` which should rather be # `+(::SingleVariable, ::Number)`. const TypedScalarLike{T} = Union{MOI.ScalarAffineFunction{T}, MOI.ScalarQuadraticFunction{T}} +# Used for overloading Base operator functions so `T` is not in the union to +# avoid overloading e.g. `+(::Float64, ::Float64)` +const ScalarLike{T} = Union{MOI.SingleVariable, TypedScalarLike{T}} # Functions convertible to a VectorAffineFunction const VectorAffineLike{T} = Union{Vector{T}, MOI.VectorOfVariables, MOI.VectorAffineFunction{T}} # Functions convertible to a VectorQuadraticFunction const VectorQuadraticLike{T} = Union{VectorAffineLike{T}, MOI.VectorQuadraticFunction{T}} +# `VectorLike` for which `T` is defined to avoid defining, e.g., +# `+(::VectorOfVariables, ::Any)` which should rather be +# `+(::VectorOfVariables, ::Number)`. +const TypedVectorLike{T} = Union{MOI.VectorAffineFunction{T}, + MOI.VectorQuadraticFunction{T}} # Used for overloading Base operator functions so `T` is not in the union to # avoid overloading e.g. `+(::Float64, ::Float64)` -const VectorLike{T} = Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}, - MOI.VectorQuadraticFunction{T}} +const VectorLike{T} = Union{MOI.VectorOfVariables, TypedVectorLike{T}} + +const TypedLike{T} = Union{TypedScalarLike{T}, TypedVectorLike{T}} ###################################### +/- ##################################### ## promote_operation @@ -1354,7 +1359,14 @@ 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}, f::MOI.SingleVariable, α::T) where T +function operate(::typeof(*), ::Type{T}, α::T, f::MOI.VectorOfVariables) where T + return MOI.VectorAffineFunction{T}( + [MOI.VectorAffineTerm(i, MOI.ScalarAffineTerm(α, f.variables[i])) + for i in eachindex(f.variables)], zeros(T, MOI.output_dimension(f))) +end +function operate(::typeof(*), ::Type{T}, + f::Union{MOI.SingleVariable, MOI.VectorOfVariables}, + α::T) where T return operate(*, T, α, f) end @@ -1365,12 +1377,14 @@ function operate!(::typeof(*), ::Type{T}, f.constant *= α return f end -function operate(::typeof(*), ::Type{T}, α::T, f::MOI.ScalarAffineFunction) where T - return operate!(*, T, copy(f), α) +function operate!(::typeof(*), ::Type{T}, + f::Union{MOI.VectorAffineFunction{T}, + MOI.VectorQuadraticFunction{T}}, α::T) where T + map_terms!(term -> operate_term(*, α, term), f) + rmul!(f.constants, α) + return f end - -function operate(::typeof(*), ::Type{T}, α::T, - f::MOI.ScalarQuadraticFunction) where T +function operate(::typeof(*), ::Type{T}, α::T, f::TypedLike{T}) where T return operate!(*, T, copy(f), α) end @@ -1439,16 +1453,16 @@ end function Base.:*(arg::ScalarLike{T}, args::ScalarLike{T}...) where T return operate(*, T, arg, args...) end -function Base.:*(f::T, g::TypedScalarLike{T}) where T +function Base.:*(f::T, g::TypedLike{T}) where T return operate(*, T, f, g) end -function Base.:*(f::Number, g::MOI.SingleVariable) +function Base.:*(f::Number, g::Union{MOI.SingleVariable, MOI.VectorOfVariables}) return operate(*, typeof(f), f, g) end -function Base.:*(f::TypedScalarLike{T}, g::T) where T +function Base.:*(f::TypedLike{T}, g::T) where T return operate(*, T, g, f) end -function Base.:*(f::MOI.SingleVariable, g::Number) +function Base.:*(f::Union{MOI.SingleVariable, MOI.VectorOfVariables}, g::Number) return operate(*, typeof(g), f, g) end @@ -1477,13 +1491,24 @@ function operate(::typeof(/), ::Type{T}, f::MOI.SingleVariable, zero(T)) end -function operate!(::typeof(/), ::Type{T}, f::MOI.ScalarAffineFunction{T}, +function operate!(::typeof(/), ::Type{T}, + f::Union{MOI.ScalarAffineFunction{T}, + MOI.ScalarQuadraticFunction{T}}, α::T) where T - f.terms .= operate_term.(/, f.terms, α) + map_terms!(term -> operate_term(/, term, α), f) f.constant /= α return f end +function operate!(::typeof(/), ::Type{T}, + f::Union{MOI.VectorAffineFunction{T}, + MOI.VectorQuadraticFunction{T}}, + α::T) where T + map_terms!(term -> operate_term(/, term, α), f) + rmul!(f.constants, inv(α)) + return f +end + function operate!(::typeof(/), ::Type{T}, f::MOI.ScalarQuadraticFunction{T}, α::T) where T f.affine_terms .= operate_term.(/, f.affine_terms, α) @@ -1492,16 +1517,19 @@ function operate!(::typeof(/), ::Type{T}, f::MOI.ScalarQuadraticFunction{T}, return f end -function operate(::typeof(/), ::Type{T}, - f::Union{MOI.ScalarAffineFunction{T}, - MOI.ScalarQuadraticFunction{T}}, α::T) where T +function operate(::typeof(/), ::Type{T}, f::TypedLike{T}, α::T) where T return operate!(/, T, copy(f), α) end +function operate(::typeof(/), ::Type{T}, + f::Union{MOI.SingleVariable, MOI.VectorOfVariables}, + α::T) where T + return operate(*, T, inv(α), f) +end -function Base.:/(f::TypedScalarLike{T}, g::T) where T +function Base.:/(f::TypedLike{T}, g::T) where T return operate(/, T, f, g) end -function Base.:/(f::MOI.SingleVariable, g::Number) +function Base.:/(f::Union{MOI.SingleVariable, MOI.VectorOfVariables}, g::Number) return operate(/, typeof(g), f, g) end diff --git a/test/Utilities/functions.jl b/test/Utilities/functions.jl index e3224f12b0..8b6059b593 100644 --- a/test/Utilities/functions.jl +++ b/test/Utilities/functions.jl @@ -680,59 +680,90 @@ end @test MOIU.promote_operation(-, T, t) == MOI.VectorQuadraticFunction{T} end - α = [1, 2, 3] - v = MOI.VectorOfVariables([y, w, y]) - g = MOI.VectorAffineFunction( - MOI.VectorAffineTerm.([3, 1], - MOI.ScalarAffineTerm.([5, 2], [y, x])), - [3, 1, 4]) - f = MOI.VectorQuadraticFunction( + @testset "Vector + and - Vector" begin + α = [1, 2, 3] + v = MOI.VectorOfVariables([y, w, y]) + g = MOI.VectorAffineFunction( + MOI.VectorAffineTerm.([3, 1], + MOI.ScalarAffineTerm.([5, 2], [y, x])), + [3, 1, 4]) + f = MOI.VectorQuadraticFunction( MOI.VectorAffineTerm.([1, 2, 2], MOI.ScalarAffineTerm.([3, 1, 2], [x, x, y])), MOI.VectorQuadraticTerm.([1, 1, 2], MOI.ScalarQuadraticTerm.([1, 2, 3], [x, y, x], [x, y, y])), [7, 3, 4]) - v_plus_g = MOI.VectorAffineFunction( - MOI.VectorAffineTerm.([3, 1, 1, 2, 3], - MOI.ScalarAffineTerm.([5, 2, 1, 1, 1], [y, x, y, w, y])), - [3, 1, 4]) - g_plus_α = MOI.VectorAffineFunction( - MOI.VectorAffineTerm.([3, 1], - MOI.ScalarAffineTerm.([5, 2], [y, x])), - [4, 3, 7]) - α_minus_v = MOI.VectorAffineFunction( - MOI.VectorAffineTerm.([1, 2, 3], - MOI.ScalarAffineTerm.([-1, -1, -1], [y, w, y])), - [1, 2, 3]) - v_minus_v_plus_v = MOI.VectorAffineFunction( - MOI.VectorAffineTerm.([1, 2, 3, 1, 2, 3, 1, 2, 3], - MOI.ScalarAffineTerm.([1, 1, 1, -1, -1, -1, 1, 1, 1], - [y, w, y, y, w, y, y, w, y])), - [0, 0, 0]) - f_plus_α = MOI.VectorQuadraticFunction( - MOI.VectorAffineTerm.([1, 2, 2], - MOI.ScalarAffineTerm.([3, 1, 2], [x, x, y])), - MOI.VectorQuadraticTerm.([1, 1, 2], - MOI.ScalarQuadraticTerm.([1, 2, 3], [x, y, x], [x, y, y])), - [8, 5, 7]) - f_minus_g = MOI.VectorQuadraticFunction( - MOI.VectorAffineTerm.([1, 2, 2, 3, 1], - MOI.ScalarAffineTerm.([3, 1, 2, -5, -2], [x, x, y, y, x])), + v_plus_g = MOI.VectorAffineFunction( + MOI.VectorAffineTerm.([3, 1, 1, 2, 3], + MOI.ScalarAffineTerm.([5, 2, 1, 1, 1], [y, x, y, w, y])), + [3, 1, 4]) + g_plus_α = MOI.VectorAffineFunction( + MOI.VectorAffineTerm.([3, 1], + MOI.ScalarAffineTerm.([5, 2], [y, x])), + [4, 3, 7]) + α_minus_v = MOI.VectorAffineFunction( + MOI.VectorAffineTerm.([1, 2, 3], + MOI.ScalarAffineTerm.([-1, -1, -1], [y, w, y])), + [1, 2, 3]) + v_minus_v_plus_v = MOI.VectorAffineFunction( + MOI.VectorAffineTerm.([1, 2, 3, 1, 2, 3, 1, 2, 3], + MOI.ScalarAffineTerm.([1, 1, 1, -1, -1, -1, 1, 1, 1], + [y, w, y, y, w, y, y, w, y])), + [0, 0, 0]) + f_plus_α = MOI.VectorQuadraticFunction( + MOI.VectorAffineTerm.([1, 2, 2], + MOI.ScalarAffineTerm.([3, 1, 2], [x, x, y])), MOI.VectorQuadraticTerm.([1, 1, 2], MOI.ScalarQuadraticTerm.([1, 2, 3], [x, y, x], [x, y, y])), - [4, 2, 0]) - @test v + g ≈ v_plus_g - @test g + α ≈ g_plus_α - @test α + g ≈ g_plus_α - @test α - v ≈ α_minus_v - @test MOIU.operate(+, Int, MOIU.operate(-, Int, v, v), v) ≈ v_minus_v_plus_v - @test f + α ≈ f_plus_α - @test f - g ≈ f_minus_g - @test f - f + f - g ≈ f_minus_g - @test v + f + α - v ≈ f_plus_α - @test v - f - α - v ≈ - f_plus_α - @test MOIU.operate!(-, Int, v, f) - v ≈ - f - @test (g + v + g + v + f) - (v + g + v + g) ≈ f - @test v - α ≈ - α_minus_v - @test g - f ≈ - f_minus_g + [8, 5, 7]) + f_minus_g = MOI.VectorQuadraticFunction( + MOI.VectorAffineTerm.([1, 2, 2, 3, 1], + MOI.ScalarAffineTerm.([3, 1, 2, -5, -2], [x, x, y, y, x])), + MOI.VectorQuadraticTerm.([1, 1, 2], + MOI.ScalarQuadraticTerm.([1, 2, 3], [x, y, x], [x, y, y])), + [4, 2, 0]) + @test v + g ≈ v_plus_g + @test g + α ≈ g_plus_α + @test α + g ≈ g_plus_α + @test α - v ≈ α_minus_v + @test MOIU.operate(+, Int, MOIU.operate(-, Int, v, v), v) ≈ v_minus_v_plus_v + @test f + α ≈ f_plus_α + @test f - g ≈ f_minus_g + @test f - f + f - g ≈ f_minus_g + @test v + f + α - v ≈ f_plus_α + @test v - f - α - v ≈ - f_plus_α + @test MOIU.operate!(-, Int, v, f) - v ≈ - f + @test (g + v + g + v + f) - (v + g + v + g) ≈ f + @test v - α ≈ - α_minus_v + @test g - f ≈ - f_minus_g + end + @testset "Vector * and / constant" begin + v = MOI.VectorOfVariables([y, w, y]) + v2 = MOIU.operate( + vcat, Float64, + 2.0fy, + 2.0fw, + 2.0fy) + f = MOIU.operate( + vcat, Float64, + 2.0fx + 3.0fy + 1.0, + 3.0fx + 2.0fy + 3.0) + f2 = MOIU.operate( + vcat, Float64, + 4.0fx + 6.0fy + 2.0, + 6.0fx + 4.0fy + 6.0) + g = MOIU.operate( + vcat, Float64, + 7.0fx * fy + 2.0fx + 3.0fy + 1.0, + 6.0fx * fx + 5.0fy * fy + 3.0fx + 2.0fy + 3.0) + g2 = MOIU.operate( + vcat, Float64, + 14.0fx * fy + 4.0fx + 6.0fy + 2.0, + 12.0fx * fx + 10.0fy * fy + 6.0fx + 4.0fy + 6.0) + @testset "$(typeof(a))" for (a, a2) in [(v, v2), (f, f2), (g, g2)] + @test a * 2.0 ≈ a2 + @test 2.0 * a ≈ a2 + @test a / 0.5 ≈ a2 + end + end end