diff --git a/perf/matrix_of_constraints.jl b/perf/matrix_of_constraints.jl new file mode 100644 index 0000000000..5c062709d3 --- /dev/null +++ b/perf/matrix_of_constraints.jl @@ -0,0 +1,80 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +import SparseArrays +using BenchmarkTools + +import MathOptInterface as MOI + +# Inspired from MatrixOfConstraints +MOI.Utilities.@product_of_sets(_EqualTos, MOI.EqualTo{T},) + +a = 1 + +function _equality_constraints( + A::AbstractMatrix{T}, + b::AbstractVector{T}, +) where {T} + sets = _EqualTos{T}() + for _ in eachindex(b) + MOI.Utilities.add_set( + sets, + MOI.Utilities.set_index(sets, MOI.EqualTo{T}), + ) + end + MOI.Utilities.final_touch(sets) + constants = MOI.Utilities.Hyperrectangle(b, b) + model = MOI.Utilities.MatrixOfConstraints{T}(A, constants, sets) + model.final_touch = true + return model +end + +function _free_variables(n, ::Type{T}) where {T} + model = MOI.Utilities.VariablesContainer{T}() + for _ in 1:n + MOI.add_variable(model) + end + return model +end + +function lp_standard_form(m, n; p = 0.1, tr = false) + T = Float64 + A = SparseArrays.sprand(T, m, n, p) + if tr + A = copy(A')' + end + b = rand(m) + return MOI.Utilities.GenericModel{T}( + MOI.Utilities.ObjectiveContainer{T}(), + _free_variables(n, T), + _equality_constraints(A, b), + ) +end + +function bench(m, args...; kws...) + T = Float64 + model = lp_standard_form(m, args...; kws...) + ci = MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}( + rand(1:m), + ) + return @benchmark MOI.get($model, MOI.ConstraintFunction(), $ci) +end + +bench(10, 100000) +bench(10, 100000, tr = true) + +function prof(m, args...) + T = Float64 + model = lp_standard_form(m, args...) + return @profview for i in 1:m + ci = MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}( + rand(1:m), + ) + MOI.get(model, MOI.ConstraintFunction(), ci) + end +end + +prof(10, 100000) diff --git a/src/Utilities/sparse_matrix.jl b/src/Utilities/sparse_matrix.jl index c1ec5f177e..2f7ab5a032 100644 --- a/src/Utilities/sparse_matrix.jl +++ b/src/Utilities/sparse_matrix.jl @@ -220,6 +220,31 @@ function extract_function( return func end +function extract_function(A::LinearAlgebra.Transpose, row::Integer, constant) + return _extract_column_as_function(parent(A), row, constant, identity) +end + +function extract_function(A::LinearAlgebra.Adjoint, row::Integer, constant) + return _extract_column_as_function(parent(A), row, constant, conj) +end + +function _extract_column_as_function( + A::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}}, + col::Integer, + constant::T, + value_map::F, +) where {T,F<:Function} + func = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], constant) + for i in SparseArrays.nzrange(A, col) + row = _shift(A.rowval[i], _indexing(A), OneBasedIndexing()) + push!( + func.terms, + MOI.ScalarAffineTerm(value_map(A.nzval[i]), MOI.VariableIndex(row)), + ) + end + return func +end + function extract_function( A::Union{MutableSparseMatrixCSC{T},SparseArrays.SparseMatrixCSC{T}}, rows::UnitRange, diff --git a/test/Utilities/matrix_of_constraints.jl b/test/Utilities/matrix_of_constraints.jl index e6c5eec4a8..0f48a2c24b 100644 --- a/test/Utilities/matrix_of_constraints.jl +++ b/test/Utilities/matrix_of_constraints.jl @@ -9,6 +9,7 @@ module TestMatrixOfConstraints using Test import MathOptInterface as MOI +import LinearAlgebra import SparseArrays function runtests() @@ -741,27 +742,33 @@ function test_lp_standard_form() minobjective: 7x1 + 8x2 cx1: x1 >= 0.0 cx2: x2 >= 0.0 - c1: 1x1 == 5.0 - c2: 3x1 + 4x2 == 6.0 + c1::Complex{Float64}: (1 - 3im) * x1 == 5.0 - im + c2::Complex{Float64}: (3 + 2im) * x1 + 4x2 == 6.0 + im """ - expected = MOI.Utilities.Model{Float64}() + expected = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) MOI.Utilities.loadfromstring!(expected, s) var_names = ["x1", "x2"] con_names = ["c1", "c2"] - A = SparseArrays.sparse([1.0 0.0; 3.0 4.0]) - b = [5.0, 6.0] - form = MOI.Utilities.GenericModel{Float16}( - expected.objective, - expected.variables, - _equality_constraints(A, b), - ) - model = MOI.Utilities.Model{Float64}() - MOI.copy_to(MOI.Bridges.Constraint.Scalarize{Float64}(model), form) - MOI.set(model, MOI.VariableName(), MOI.VariableIndex.(1:2), var_names) - F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64} - ci = MOI.ConstraintIndex{F,S}.(1:2) - MOI.set(model, MOI.ConstraintName(), ci, con_names) - MOI.Test.util_test_models_equal(model, expected, var_names, con_names) + A = SparseArrays.sparse([1.0 - 3im 0.0; 3.0 + 2im 4.0]) + b = [5.0 - im, 6.0 + im] + for _A in [ + A, + LinearAlgebra.transpose(copy(LinearAlgebra.transpose(A))), + (copy(A'))', + ] + form = MOI.Utilities.GenericModel{Float64}( + expected.model.objective, + expected.model.variables, + _equality_constraints(_A, b), + ) + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + MOI.copy_to(MOI.Bridges.Constraint.Scalarize{ComplexF64}(model), form) + MOI.set(model, MOI.VariableName(), MOI.VariableIndex.(1:2), var_names) + F, S = MOI.ScalarAffineFunction{ComplexF64}, MOI.EqualTo{ComplexF64} + ci = MOI.ConstraintIndex{F,S}.(1:2) + MOI.set(model, MOI.ConstraintName(), ci, con_names) + MOI.Test.util_test_models_equal(model, expected, var_names, con_names) + end return end