From c6ecc1df0cf4434decf0ac9c57d79fa51c738be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 20 Dec 2025 08:55:12 +0100 Subject: [PATCH 01/11] Simplify conic --- src/MatrixOptInterface.jl | 1 - src/sparse_matrix.jl | 108 -------------------------------------- test/conic_form.jl | 23 +------- 3 files changed, 2 insertions(+), 130 deletions(-) delete mode 100644 src/sparse_matrix.jl diff --git a/src/MatrixOptInterface.jl b/src/MatrixOptInterface.jl index 951fde2..145daf2 100644 --- a/src/MatrixOptInterface.jl +++ b/src/MatrixOptInterface.jl @@ -65,7 +65,6 @@ end @enum VariableType CONTINUOUS INTEGER BINARY -include("sparse_matrix.jl") include("conic_form.jl") include("matrix_input.jl") #include("change_form.jl") diff --git a/src/sparse_matrix.jl b/src/sparse_matrix.jl deleted file mode 100644 index 2c4354c..0000000 --- a/src/sparse_matrix.jl +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2019: Joaquim Dias Garcia, and contributors -# -# 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. - -abstract type AbstractIndexing end - -struct ZeroBasedIndexing <: AbstractIndexing end - -struct OneBasedIndexing <: AbstractIndexing end - -first_index(::ZeroBasedIndexing) = 0 - -first_index(::OneBasedIndexing) = 1 - -shift(x, ::ZeroBasedIndexing, ::ZeroBasedIndexing) = x - -shift(x::Integer, ::ZeroBasedIndexing, ::OneBasedIndexing) = x + 1 - -shift(x::Array{<:Integer}, ::ZeroBasedIndexing, ::OneBasedIndexing) = x .+ 1 - -shift(x::Integer, ::OneBasedIndexing, ::ZeroBasedIndexing) = x - 1 - -shift(x, ::OneBasedIndexing, ::OneBasedIndexing) = x - -mutable struct SparseMatrixCSRtoCSC{Tv,Ti<:Integer,I<:AbstractIndexing} - indexing::I - m::Int # Number of rows - n::Int # Number of columns - colptr::Vector{Ti} - rowval::Vector{Ti} - nzval::Vector{Tv} - function SparseMatrixCSRtoCSC{Tv,Ti,I}(n) where {Tv,Ti<:Integer,I} - A = new{Tv,Ti,I}() - A.n = n - A.colptr = zeros(Ti, n + 1) - return A - end -end - -function allocate_nonzeros(A::SparseMatrixCSRtoCSC{Tv,Ti}) where {Tv,Ti} - for i in 3:length(A.colptr) - A.colptr[i] += A.colptr[i-1] - end - A.rowval = Vector{Ti}(undef, A.colptr[end]) - A.nzval = Vector{Tv}(undef, A.colptr[end]) - return -end - -function final_touch(A::SparseMatrixCSRtoCSC) - for i in length(A.colptr):-1:2 - A.colptr[i] = shift(A.colptr[i-1], ZeroBasedIndexing(), A.indexing) - end - A.colptr[1] = first_index(A.indexing) - return -end - -function _allocate_terms(colptr, indexmap, terms) - for term in terms - colptr[indexmap[term.scalar_term.variable].value+1] += 1 - end - return -end - -function allocate_terms(A::SparseMatrixCSRtoCSC, indexmap, func) - return _allocate_terms(A.colptr, indexmap, func.terms) -end - -function _load_terms(colptr, rowval, nzval, indexmap, terms, offset) - for term in terms - ptr = colptr[indexmap[term.scalar_term.variable].value] += 1 - rowval[ptr] = offset + term.output_index - nzval[ptr] = -term.scalar_term.coefficient - end - return -end - -function load_terms(A::SparseMatrixCSRtoCSC, indexmap, func, offset) - return _load_terms( - A.colptr, - A.rowval, - A.nzval, - indexmap, - func.terms, - shift(offset, OneBasedIndexing(), A.indexing), - ) -end - -""" - Base.convert(::Type{SparseMatrixCSC{Tv, Ti}}, A::SparseMatrixCSRtoCSC{Tv, Ti, I}) where {Tv, Ti, I} - -Converts `A` to a `SparseMatrixCSC`. Note that the field `A.nzval` is **not -copied** so if `A` is modified after the call of this function, it can still -affect the value returned. Moreover, if `I` is `OneBasedIndexing`, `colptr` -and `rowval` are not copied either, i.e., the conversion is allocation-free. -""" -function Base.convert( - ::Type{SparseMatrixCSC{Tv,Ti}}, - A::SparseMatrixCSRtoCSC{Tv,Ti}, -) where {Tv,Ti} - return SparseMatrixCSC{Tv,Ti}( - A.m, - A.n, - shift(A.colptr, A.indexing, OneBasedIndexing()), - shift(A.rowval, A.indexing, OneBasedIndexing()), - A.nzval, - ) -end diff --git a/test/conic_form.jl b/test/conic_form.jl index dbebee1..3b2e198 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -11,25 +11,6 @@ function _test_matrix_equal(A::SparseMatrixCSC, B::SparseMatrixCSC) @test A.colptr == B.colptr end -function _test_matrix_equal( - A::MatOI.SparseMatrixCSRtoCSC{Tv,Ti,I}, - B::SparseMatrixCSC, -) where {Tv,Ti,I} - @test A.m == B.m - @test A.n == B.n - @test A.nzval ≈ B.nzval atol = ATOL rtol = RTOL - if I <: MatOI.OneBasedIndexing - @test A.rowval == B.rowval - @test A.colptr == B.colptr - else - @test A.rowval == B.rowval .- 1 - @test A.colptr == B.colptr .- 1 - end - sA = convert(typeof(B), A) - @test typeof(sA) == typeof(B) - return _test_matrix_equal(sA, B) -end - # _psd1test: https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/contconic.jl#L2417 function psd1(::Type{T}, ::Type{I}) where {T,I} # We use `MockOptimizer` to have indices xor'ed so that it tests that we don't assumes they are `1:n`. @@ -93,7 +74,7 @@ function psd1(::Type{T}, ::Type{I}) where {T,I} conic_form = MatOI.GeometricConicForm{ T, - MatOI.SparseMatrixCSRtoCSC{T,Int,I}, + MOI.Utilities.MutableSparseMatrixCSC{T,Int,I}, Vector{T}, }([ MOI.PositiveSemidefiniteConeTriangle, @@ -319,7 +300,7 @@ function psd2( end @testset "PSD $T, $I" for T in [Float64, BigFloat], - I in [MatOI.ZeroBasedIndexing, MatOI.OneBasedIndexing] + I in [MOI.Utilities.ZeroBasedIndexing, MOI.Utilities.OneBasedIndexing] psd1(T, I) psd2(T, I) From d46d9407717cdd36cf5f2d210830e5aa5ca39d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 08:50:51 +0100 Subject: [PATCH 02/11] Fixes --- src/MatrixOptInterface.jl | 1 + src/conic_form.jl | 263 ++++---------------------------------- src/product_of_sets.jl | 75 +++++++++++ test/conic_form.jl | 46 +++---- 4 files changed, 127 insertions(+), 258 deletions(-) create mode 100644 src/product_of_sets.jl diff --git a/src/MatrixOptInterface.jl b/src/MatrixOptInterface.jl index 145daf2..80c5a73 100644 --- a/src/MatrixOptInterface.jl +++ b/src/MatrixOptInterface.jl @@ -65,6 +65,7 @@ end @enum VariableType CONTINUOUS INTEGER BINARY +include("product_of_sets.jl") include("conic_form.jl") include("matrix_input.jl") #include("change_form.jl") diff --git a/src/conic_form.jl b/src/conic_form.jl index 0edb1c1..9432993 100644 --- a/src/conic_form.jl +++ b/src/conic_form.jl @@ -4,7 +4,7 @@ # in the LICENSE.md file or at https://opensource.org/licenses/MIT. """ - GeometricConicForm{T, AT, VT, C} <: MOI.ModelLike + empty_geometric_conic_form Represents an optimization model of the form: ``` @@ -13,246 +13,37 @@ s.t. b_i - A_i x ∈ C_i ∀ i ``` with each `C_i` a cone defined in MOI. """ -mutable struct GeometricConicForm{T,AT,VB,VC,C} <: MOI.ModelLike - num_rows::Vector{Int} - dimension::Dict{Int,Int} - sense::MOI.OptimizationSense - objective_constant::T # The objective - A::Union{Nothing,AT} # The constraints - b::VB # `b - Ax in cones` - c::VC # `sense c'x + objective_constant` - cone_types::C - cone_types_dict::Dict{DataType,Int} - - function GeometricConicForm{T,AT,VB,VC}(cone_types) where {T,AT,VB,VC} - model = new{T,AT,VB,VC,typeof(cone_types)}() - model.cone_types = cone_types - model.cone_types_dict = - Dict{DataType,Int}(s => i for (i, s) in enumerate(cone_types)) - model.num_rows = zeros(Int, length(cone_types)) - model.dimension = Dict{Int,Int}() - model.A = nothing - return model - end -end - -function GeometricConicForm{T,AT,VT}(cone_types) where {T,AT,VT} - return GeometricConicForm{T,AT,VT,VT}(cone_types) -end - -_set_type(::MOI.ConstraintIndex{F,S}) where {F,S} = S - -MOI.is_empty(model::GeometricConicForm) = model.A === nothing - -function MOI.empty!(model::GeometricConicForm{T}) where {T} - empty!(model.dimension) - fill!(model.num_rows, 0) - model.A = nothing - model.sense = MOI.FEASIBILITY_SENSE - return model.objective_constant = zero(T) -end - -function MOI.supports_constraint( - model::GeometricConicForm{T}, - ::Type{MOI.VectorAffineFunction{T}}, - ::Type{S}, -) where {T,S<:MOI.AbstractVectorSet} - return haskey(model.cone_types_dict, S) -end - -function _allocate_variables( - model::GeometricConicForm{T,AT,VT}, - vis_src, - idxmap, -) where {T,AT,VT} - model.A = AT(length(vis_src)) - for (i, vi) in enumerate(vis_src) - idxmap[vi] = MOI.VariableIndex(i) - end - return -end - -function rows( - model::GeometricConicForm{T}, - ci::CI{MOI.VectorAffineFunction{T}}, -) where {T} - return ci.value .+ (1:model.dimension[ci.value]) -end - -function MOI.set( - ::GeometricConicForm, - ::MOI.VariablePrimalStart, - ::MOI.VariableIndex, - ::Nothing, -) - return -end - -function MOI.set( - model::GeometricConicForm{T}, - ::MOI.VariablePrimalStart, - vi::MOI.VariableIndex, - value::T, -) where {T} - return model.primal[vi.value] = value -end - -function MOI.set( - ::GeometricConicForm, - ::MOI.ConstraintPrimalStart, - ::MOI.ConstraintIndex, - ::Nothing, -) - return -end - -function MOI.set( - model::GeometricConicForm, - ::MOI.ConstraintPrimalStart, - ci::MOI.ConstraintIndex, - value, -) - offset = constroffset(model, ci) - return model.slack[rows(model, ci)] .= value -end - -function MOI.set( - ::GeometricConicForm, - ::MOI.ConstraintDualStart, - ::MOI.ConstraintIndex, - ::Nothing, -) - return -end - -function MOI.set( - model::GeometricConicForm, - ::MOI.ConstraintDualStart, - ci::MOI.ConstraintIndex, - value, -) - offset = constroffset(model, ci) - return model.dual[rows(model, ci)] .= value -end - -function MOI.set( - model::GeometricConicForm, - ::MOI.ObjectiveSense, - sense::MOI.OptimizationSense, -) - return model.sense = sense -end - -variable_index_value(t::MOI.ScalarAffineTerm) = t.variable.value - -function variable_index_value(t::MOI.VectorAffineTerm) - return variable_index_value(t.scalar_term) -end - -function MOI.set( - model::GeometricConicForm{T}, - ::MOI.ObjectiveFunction, - f::MOI.ScalarAffineFunction{T}, -) where {T} - c = Vector( - sparsevec( - variable_index_value.(f.terms), - MOI.coefficient.(f.terms), - model.A.n, - ), +function empty_geometric_conic_form(cones; Tv = Float64, Ti = Int, I = MOI.Utilities.OneBasedIndexing) + return MOI.Utilities.GenericModel{T}( + MOI.Utilities.ObjectiveContainer{T}(), + MOI.Utilities.FreeVariables(), + MOI.Utilities.MatrixOfConstraints{ + T, + MOI.Utilities.MutableSparseMatrixCSC{ + Tv, + Ti, + I, + }, + Vector{T}, + ProductOfSets{T}, + }, ) - model.objective_constant = f.constant - model.c = c - return -end - -function _allocate_constraint( - model::GeometricConicForm, - src, - indexmap, - cone_id, - ci, -) - # TODO use `CanonicalConstraintFunction` - func = MOI.get(src, MOI.ConstraintFunction(), ci) - func = MOIU.is_canonical(func) ? func : MOI.Utilities.canonical(func) - allocate_terms(model.A, indexmap, func) - offset = model.num_rows[cone_id] - model.num_rows[cone_id] = offset + MOI.output_dimension(func) - return ci, offset, func end -function _allocate_constraints( - model::GeometricConicForm{T}, - src, - indexmap, - cone_id, - ::Type{S}, -) where {T,S} - cis = MOI.get( - src, - MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T},S}(), - ) - return map(cis) do ci - return _allocate_constraint(model, src, indexmap, cone_id, ci) - end +function geometric_conic_form(model::MOI.ModelLike, cones; kws...) + form = empty_geometric_conic_form(cones; kws...) + index_map = MOI.copy_to(form, model) + return form, index_map end -function _load_variables(model::GeometricConicForm, nvars::Integer) - m = sum(model.num_rows) - model.A.m = m - model.b = zeros(m) - model.c = zeros(model.A.n) - return allocate_nonzeros(model.A) -end +_coef_type(::MOI.Utilities.AbstractModel{T}) where {T} = T -function _load_constraints( - model::GeometricConicForm, - src, - indexmap, - cone_offset, - i, - cache, -) - for (ci_src, offset_in_cone, func) in cache - offset = cone_offset + offset_in_cone - set = MOI.get(src, MOI.ConstraintSet(), ci_src) - load_terms(model.A, indexmap, func, offset) - copyto!(model.b, offset + 1, func.constants) - model.dimension[offset] = MOI.output_dimension(func) - indexmap[ci_src] = typeof(ci_src)(offset) - end -end - -function MOI.copy_to(dest::GeometricConicForm{T}, src::MOI.ModelLike) where {T} - MOI.empty!(dest) - vis_src = MOI.get(src, MOI.ListOfVariableIndices()) - idxmap = MOIU.IndexMap() - has_constraints = BitSet() - for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) - i = get(dest.cone_types_dict, S, nothing) - if i === nothing || F != MOI.VectorAffineFunction{T} - throw(MOI.UnsupportedConstraint{F,S}()) - end - push!(has_constraints, i) - end - _allocate_variables(dest, vis_src, idxmap) - # Allocate constraints - caches = map(collect(has_constraints)) do i - return _allocate_constraints(dest, src, idxmap, i, dest.cone_types[i]) - end - # Load variables - _load_variables(dest, length(vis_src)) - # Set variable attributes - MOIU.pass_attributes(dest, src, idxmap, vis_src) - # Set model attributes - MOIU.pass_attributes(dest, src, idxmap) - # Load constraints - offset = 0 - for (i, cache) in zip(has_constraints, caches) - _load_constraints(dest, src, idxmap, offset, i, cache) - offset += dest.num_rows[i] +function objective_vector(model::MOI.ModelLike; T = _coef_type(model)) + obj = MOI.get(src, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()) + dest.objective_constant = MOI.constant(obj) + c = zeros(A.n) + for term in obj.terms + c[term.variable.value] += term.coefficient end - final_touch(dest.A) - return idxmap + return c end diff --git a/src/product_of_sets.jl b/src/product_of_sets.jl new file mode 100644 index 0000000..1a6a1df --- /dev/null +++ b/src/product_of_sets.jl @@ -0,0 +1,75 @@ +# Copyright (c) 2019: Joaquim Dias Garcia, and contributors +# +# 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. +# Copied from DiffOpt + +""" + ProductOfSets{T} <: MOI.Utilities.OrderedProductOfSets{T} + +The `MOI.Utilities.@product_of_sets` macro requires to know the list of sets +at compile time. In DiffOpt however, the list depends on what the user is going +to use as set as DiffOpt supports any set as long as it implements the +required function of MathOptSetDistances. +For this type, the list of sets can be given a run-time. +""" +mutable struct ProductOfSets{T} <: MOI.Utilities.OrderedProductOfSets{T} + """ + During the copy, this counts the number of rows corresponding to + each set. At the end of copy, `final_touch` is called, which + converts this list into a cumulative ordering. + """ + num_rows::Vector{Int} + + """ + A dictionary which maps the `set_index` and `offset` of a set to the + dimension, i.e., `dimension[(set_index,offset)] → dim`. + """ + dimension::Dict{Tuple{Int,Int},Int} + + """ + A sanity bit to check that we don't call functions out-of-order. + """ + final_touch::Bool + + set_types::Vector{Type} + set_types_dict::Dict{Type,Int} + + function ProductOfSets{T}() where {T} + return new( + Int[], + Dict{Tuple{Int,Int},Int}(), + false, + Type[], + Dict{Type,Int}(), + ) + end +end + +function MOI.Utilities.set_index(set::ProductOfSets, S::Type{<:MOI.AbstractSet}) + return get(set.set_types_dict, S, nothing) +end + +MOI.Utilities.set_types(set::ProductOfSets) = set.set_types + +function set_set_types(set::ProductOfSets, set_types) + resize!(set.num_rows, length(set_types)) + fill!(set.num_rows, 0) + resize!(set.set_types, length(set_types)) + copy!(set.set_types, set_types) + empty!(set.set_types_dict) + for i in eachindex(set_types) + set.set_types_dict[set_types[i]] = i + end + return +end + +function add_set_types(set::ProductOfSets, S::Type) + if !haskey(set.set_types_dict, S) + push!(set.num_rows, 0) + push!(set.set_types, S) + set.set_types_dict[S] = length(set.set_types) + return true + end + return false +end diff --git a/test/conic_form.jl b/test/conic_form.jl index 3b2e198..d0bc6be 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -72,21 +72,22 @@ function psd1(::Type{T}, ::Type{I}) where {T,I} ) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - conic_form = MatOI.GeometricConicForm{ - T, - MOI.Utilities.MutableSparseMatrixCSC{T,Int,I}, - Vector{T}, - }([ - MOI.PositiveSemidefiniteConeTriangle, - MOI.SecondOrderCone, - MOI.Zeros, - ]) - index_map = MOI.copy_to(conic_form, model) + conic_form, index_map = MatOI.geometric_conic_form( + model, + [ + MOI.PositiveSemidefiniteConeTriangle, + MOI.SecondOrderCone, + MOI.Zeros, + ]; + Tv = T, + I, + ) + @test index_map isa MOI.Utilities.IndexMap - @test conic_form.c' ≈ T[2 2 2 0 2 2 1 0 0] - @test conic_form.b' ≈ T[0 0 0 0 0 0 0 0 0 -1 -inv(T(2))] + @test MatOI.objective_vector(conic_form)' ≈ T[2 2 2 0 2 2 1 0 0] + @test conic_form.constraints.constants' ≈ T[0 0 0 0 0 0 0 0 0 -1 -inv(T(2))] return _test_matrix_equal( - conic_form.A, + conic_form.constraints.coefficients, SparseMatrixCSC( 11, 9, @@ -224,17 +225,18 @@ function psd2( ) MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - conic_form = MatOI.GeometricConicForm{ - T, - MatOI.SparseMatrixCSRtoCSC{T,Int,I}, - Vector{T}, - }([MOI.Nonnegatives, MOI.Zeros, MOI.PositiveSemidefiniteConeTriangle]) - index_map = MOI.copy_to(conic_form, model) + conic_form, index_map = MatOI.geometric_conic_form( + model, + [MOI.Nonnegatives, MOI.Zeros, MOI.PositiveSemidefiniteConeTriangle]; + Tv = T, + I, + ) + @test index_map isa MOI.Utilities.IndexMap - @test conic_form.c ≈ [zeros(T, 6); one(T)] - @test conic_form.b ≈ [T(10); zeros(T, 10)] + @test MatOI.objective_vector(conic_form) ≈ [zeros(T, 6); one(T)] + @test conic_form.constraint.constants ≈ [T(10); zeros(T, 10)] return _test_matrix_equal( - conic_form.A, + conic_form.constraint.coefficients, SparseMatrixCSC( 11, 7, From 4792b2e4b4c3c51270781f9ecc886845719f111d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 15:10:10 +0100 Subject: [PATCH 03/11] Fixes --- src/conic_form.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/conic_form.jl b/src/conic_form.jl index 9432993..28cda9b 100644 --- a/src/conic_form.jl +++ b/src/conic_form.jl @@ -14,20 +14,22 @@ s.t. b_i - A_i x ∈ C_i ∀ i with each `C_i` a cone defined in MOI. """ function empty_geometric_conic_form(cones; Tv = Float64, Ti = Int, I = MOI.Utilities.OneBasedIndexing) - return MOI.Utilities.GenericModel{T}( - MOI.Utilities.ObjectiveContainer{T}(), + model = MOI.Utilities.GenericModel{Tv}( + MOI.Utilities.ObjectiveContainer{Tv}(), MOI.Utilities.FreeVariables(), MOI.Utilities.MatrixOfConstraints{ - T, + Tv, MOI.Utilities.MutableSparseMatrixCSC{ Tv, Ti, I, }, - Vector{T}, - ProductOfSets{T}, - }, + Vector{Tv}, + ProductOfSets{Tv}, + }(), ) + set_set_types(model.constraints.sets, cones) + return model end function geometric_conic_form(model::MOI.ModelLike, cones; kws...) From 10155e793b24f6b3b91eb12b8ff3b7029431371d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 15:10:14 +0100 Subject: [PATCH 04/11] Fix format --- src/conic_form.jl | 13 +++++++------ test/conic_form.jl | 6 +----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/conic_form.jl b/src/conic_form.jl index 28cda9b..f0acb11 100644 --- a/src/conic_form.jl +++ b/src/conic_form.jl @@ -13,17 +13,18 @@ s.t. b_i - A_i x ∈ C_i ∀ i ``` with each `C_i` a cone defined in MOI. """ -function empty_geometric_conic_form(cones; Tv = Float64, Ti = Int, I = MOI.Utilities.OneBasedIndexing) +function empty_geometric_conic_form( + cones; + Tv = Float64, + Ti = Int, + I = MOI.Utilities.OneBasedIndexing, +) model = MOI.Utilities.GenericModel{Tv}( MOI.Utilities.ObjectiveContainer{Tv}(), MOI.Utilities.FreeVariables(), MOI.Utilities.MatrixOfConstraints{ Tv, - MOI.Utilities.MutableSparseMatrixCSC{ - Tv, - Ti, - I, - }, + MOI.Utilities.MutableSparseMatrixCSC{Tv,Ti,I}, Vector{Tv}, ProductOfSets{Tv}, }(), diff --git a/test/conic_form.jl b/test/conic_form.jl index d0bc6be..78b56e1 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -74,11 +74,7 @@ function psd1(::Type{T}, ::Type{I}) where {T,I} conic_form, index_map = MatOI.geometric_conic_form( model, - [ - MOI.PositiveSemidefiniteConeTriangle, - MOI.SecondOrderCone, - MOI.Zeros, - ]; + [MOI.PositiveSemidefiniteConeTriangle, MOI.SecondOrderCone, MOI.Zeros]; Tv = T, I, ) From 060653dda7dd52d1cabf04c5b603abea7c8dc254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 15:19:47 +0100 Subject: [PATCH 05/11] Fix --- src/conic_form.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/conic_form.jl b/src/conic_form.jl index f0acb11..107823c 100644 --- a/src/conic_form.jl +++ b/src/conic_form.jl @@ -42,9 +42,8 @@ end _coef_type(::MOI.Utilities.AbstractModel{T}) where {T} = T function objective_vector(model::MOI.ModelLike; T = _coef_type(model)) - obj = MOI.get(src, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()) - dest.objective_constant = MOI.constant(obj) - c = zeros(A.n) + obj = MOI.get(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()) + c = zeros(MOI.get(model, MOI.NumberOfVariables())) for term in obj.terms c[term.variable.value] += term.coefficient end From 1d8c4cb9a522ff54bc3a490eefed00b694de0016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 18:18:51 +0100 Subject: [PATCH 06/11] Fix --- test/conic_form.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conic_form.jl b/test/conic_form.jl index 78b56e1..6571ac2 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -3,7 +3,7 @@ # 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. -function _test_matrix_equal(A::SparseMatrixCSC, B::SparseMatrixCSC) +function _test_matrix_equal(A::MOI.Utilities.MutableSparseMatrixCSC, B::SparseMatrixCSC) @test A.m == B.m @test A.n == B.n @test A.nzval ≈ B.nzval atol = ATOL rtol = RTOL From fbc498cc0b32c205f744a1311373e56d45793095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 27 Feb 2026 18:23:21 +0100 Subject: [PATCH 07/11] Flip --- src/conic_form.jl | 2 +- test/conic_form.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conic_form.jl b/src/conic_form.jl index 107823c..03fe18f 100644 --- a/src/conic_form.jl +++ b/src/conic_form.jl @@ -9,7 +9,7 @@ Represents an optimization model of the form: ``` sense ⟨c, x⟩ + c0 -s.t. b_i - A_i x ∈ C_i ∀ i +s.t. A_i x + b_i ∈ C_i ∀ i ``` with each `C_i` a cone defined in MOI. """ diff --git a/test/conic_form.jl b/test/conic_form.jl index 6571ac2..a612002 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -111,7 +111,7 @@ function psd1(::Type{T}, ::Type{I}) where {T,I} 9, 11, ], - T[ + -T[ -1, -1, -1, @@ -265,7 +265,7 @@ function psd2( 9, 11, ], - T[ + -T[ 1.0, -1.0, -0.45, From 301ba0ef02c84c973d873ae8962d0e45fc94e562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 28 Feb 2026 07:36:52 +0100 Subject: [PATCH 08/11] Fix --- test/conic_form.jl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/conic_form.jl b/test/conic_form.jl index a612002..b6eaa15 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -3,12 +3,17 @@ # 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. -function _test_matrix_equal(A::MOI.Utilities.MutableSparseMatrixCSC, B::SparseMatrixCSC) +function _test_matrix_equal( + A::MOI.Utilities.MutableSparseMatrixCSC, + B::SparseMatrixCSC, + I, +) @test A.m == B.m @test A.n == B.n @test A.nzval ≈ B.nzval atol = ATOL rtol = RTOL - @test A.rowval == B.rowval - @test A.colptr == B.colptr + shift = I isa MOI.Utilities.OneBasedIndexing ? 0 : 1 + @test A.rowval .+ shift == B.rowval + @test A.colptr .+ shift == B.colptr end # _psd1test: https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/contconic.jl#L2417 From ae21eb87136194ba4fb0bd91b16fe0232cc08217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 28 Feb 2026 07:40:26 +0100 Subject: [PATCH 09/11] Fix --- test/conic_form.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/conic_form.jl b/test/conic_form.jl index b6eaa15..46e9719 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -140,6 +140,7 @@ function psd1(::Type{T}, ::Type{I}) where {T,I} -1, ], ), + I, ) end @@ -299,6 +300,7 @@ function psd2( 1.0, ], ), + I, ) end From cc843f634158c4d5e8b1c29a1d4bddc9439fed0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 28 Feb 2026 07:50:52 +0100 Subject: [PATCH 10/11] Fix --- test/conic_form.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/conic_form.jl b/test/conic_form.jl index 46e9719..1353dfa 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -236,9 +236,9 @@ function psd2( @test index_map isa MOI.Utilities.IndexMap @test MatOI.objective_vector(conic_form) ≈ [zeros(T, 6); one(T)] - @test conic_form.constraint.constants ≈ [T(10); zeros(T, 10)] + @test conic_form.constraints.constants ≈ [T(10); zeros(T, 10)] return _test_matrix_equal( - conic_form.constraint.coefficients, + conic_form.constraints.coefficients, SparseMatrixCSC( 11, 7, From 7197f8ae97d0b18a3e782a7458d34eb8043e3b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 28 Feb 2026 07:55:13 +0100 Subject: [PATCH 11/11] Fix --- test/conic_form.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conic_form.jl b/test/conic_form.jl index 1353dfa..410c081 100644 --- a/test/conic_form.jl +++ b/test/conic_form.jl @@ -11,7 +11,7 @@ function _test_matrix_equal( @test A.m == B.m @test A.n == B.n @test A.nzval ≈ B.nzval atol = ATOL rtol = RTOL - shift = I isa MOI.Utilities.OneBasedIndexing ? 0 : 1 + shift = I == MOI.Utilities.OneBasedIndexing ? 0 : 1 @test A.rowval .+ shift == B.rowval @test A.colptr .+ shift == B.colptr end