Skip to content

Commit

Permalink
Simplify _indices_and_coefficients to return 0-indexed cols (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Mar 20, 2024
1 parent 00df013 commit 0d0f0dd
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 77 deletions.
4 changes: 2 additions & 2 deletions src/MOI/MOI_callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function MOI.submit(
[rhs - f.constant], # drhs
Cint[0, length(indices)], # mstart
mindex,
Cint.(indices .- 1), # mcols
indices, # mcols
coefficients,
)
@checked Lib.XPRSloadcuts(cb.callback_data.model, 1, Cint(-1), 1, mindex)
Expand Down Expand Up @@ -238,7 +238,7 @@ function MOI.submit(
[rhs - f.constant], # drhs
Cint[0, length(indices)], # mstart
mindex,
Cint.(indices .- 1), # mcols
indices, # mcols
coefficients,
)
@checked Lib.XPRSloadcuts(cb.callback_data.model, 1, Cint(-1), 1, mindex)
Expand Down
130 changes: 55 additions & 75 deletions src/MOI/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -643,13 +643,13 @@ function MOI.get(
end

function _indices_and_coefficients(
indices::AbstractVector{<:Integer},
coefficients::AbstractVector{Float64},
indices::AbstractVector{Cint},
coefficients::AbstractVector{Cdouble},
model::Optimizer,
f::MOI.ScalarAffineFunction{Float64},
)
for (i, term) in enumerate(f.terms)
indices[i] = _info(model, term.variable).column
indices[i] = Cint(_info(model, term.variable).column - 1)
coefficients[i] = term.coefficient
end
return indices, coefficients
Expand All @@ -669,15 +669,14 @@ end

function _indices_and_coefficients_indicator(
model::Optimizer,
f::MOI.VectorAffineFunction,
f::MOI.VectorAffineFunction{Float64},
)
nnz = length(f.terms) - 1
indices = Vector{Cint}(undef, nnz)
coefficients = Vector{Float64}(undef, nnz)
indices = Vector{Cint}(undef, length(f.terms) - 1)
coefficients = Vector{Float64}(undef, length(f.terms) - 1)
i = 1
for fi in f.terms
if fi.output_index != 1
indices[i] = _info(model, fi.scalar_term.variable).column
indices[i] = Cint(_info(model, fi.scalar_term.variable).column - 1)
coefficients[i] = fi.scalar_term.coefficient
i += 1
end
Expand All @@ -692,11 +691,11 @@ function _indices_and_coefficients(
indices::AbstractVector{Cint},
coefficients::AbstractVector{Float64},
model::Optimizer,
f::MOI.ScalarQuadraticFunction,
f::MOI.ScalarQuadraticFunction{Float64},
)
for (i, term) in enumerate(f.quadratic_terms)
I[i] = _info(model, term.variable_1).column
J[i] = _info(model, term.variable_2).column
I[i] = Cint(_info(model, term.variable_1).column - 1)
J[i] = Cint(_info(model, term.variable_2).column - 1)
V[i] = term.coefficient
# MOI represents objective as 0.5 x' Q x
# Example: obj = 2x^2 + x*y + y^2
Expand All @@ -721,7 +720,7 @@ function _indices_and_coefficients(
# Only for constraints, Xpress -> MOI => multiply all by 2
end
for (i, term) in enumerate(f.affine_terms)
indices[i] = _info(model, term.variable).column
indices[i] = Cint(_info(model, term.variable).column - 1)
coefficients[i] = term.coefficient
end
return
Expand Down Expand Up @@ -1206,11 +1205,9 @@ function MOI.set(
_indices_and_coefficients(model, f)
obj = zeros(length(model.variable_info))
for (i, c) in zip(affine_indices, affine_coefficients)
obj[i] = c
obj[i+1] = c
end
I .-= 1
J .-= 1
@checked Lib.XPRSchgmqobj(model.inner, Cint(length(I)), I, J, V)
@checked Lib.XPRSchgmqobj(model.inner, length(I), I, J, V)
model.objective_type = SCALAR_QUADRATIC
model.is_objective_set = true
return
Expand Down Expand Up @@ -2215,12 +2212,9 @@ function MOI.add_constraint(
f::MOI.ScalarAffineFunction{Float64},
s::SIMPLE_SCALAR_SETS,
)
F, S = typeof(f), typeof(s)
if !iszero(f.constant)
throw(
MOI.ScalarFunctionConstantNotZero{Float64,typeof(f),typeof(s)}(
f.constant,
),
)
throw(MOI.ScalarFunctionConstantNotZero{Float64,F,S}(f.constant))
end
model.last_constraint_index += 1
model.affine_constraint_info[model.last_constraint_index] =
Expand All @@ -2229,16 +2223,16 @@ function MOI.add_constraint(
sense, rhs = _sense_and_rhs(s)
@checked Lib.XPRSaddrows(
model.inner,
1,#length(_drhs),
Cint(length(indices)),#Cint(length(_mclind)),
Ref{UInt8}(sense),#_srowtype,
Ref(rhs),#_drhs,
C_NULL,#_drng,
Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}),
indices .-= 1,#Cint.(_mclind::Vector{Int}),
coefficients,#_dmatval
1, # length(_drhs)
length(indices), # length(_mclind)
Ref{UInt8}(sense), # _srowtype
Ref(rhs), # _drhs
C_NULL, # _drng
Ref{Cint}(0), # _mrstart::Vector{Int}
indices, # _mclind::Vector{Int}
coefficients, # _dmatval
)
return MOI.ConstraintIndex{typeof(f),typeof(s)}(model.last_constraint_index)
return MOI.ConstraintIndex{F,S}(model.last_constraint_index)
end

function MOI.add_constraints(
Expand All @@ -2252,20 +2246,17 @@ function MOI.add_constraints(
canonicalized_functions = MOI.Utilities.canonical.(f)
# First pass: compute number of non-zeros to allocate space.
nnz = 0
F, S = eltype(f), eltype(s)
for fi in canonicalized_functions
if !iszero(fi.constant)
throw(
MOI.ScalarFunctionConstantNotZero{Float64,eltype(f),eltype(s)}(
fi.constant,
),
)
throw(MOI.ScalarFunctionConstantNotZero{Float64,F,S}(fi.constant))
end
nnz += length(fi.terms)
end
# Initialize storage
indices = Vector{MOI.ConstraintIndex{eltype(f),eltype(s)}}(undef, length(f))
indices = Vector{MOI.ConstraintIndex{F,S}}(undef, length(f))
row_starts = Vector{Cint}(undef, length(f) + 1)
row_starts[1] = 1
row_starts[1] = 0
columns = Vector{Cint}(undef, nnz)
coefficients = Vector{Float64}(undef, nnz)
senses = Vector{Cchar}(undef, length(f))
Expand All @@ -2275,29 +2266,27 @@ function MOI.add_constraints(
senses[i], rhss[i] = _sense_and_rhs(si)
row_starts[i+1] = row_starts[i] + length(fi.terms)
_indices_and_coefficients(
view(columns, row_starts[i]:row_starts[i+1]-1),
view(coefficients, row_starts[i]:row_starts[i+1]-1),
view(columns, row_starts[i]+1:row_starts[i+1]),
view(coefficients, row_starts[i]+1:row_starts[i+1]),
model,
fi,
)
model.last_constraint_index += 1
indices[i] = MOI.ConstraintIndex{eltype(f),eltype(s)}(
model.last_constraint_index,
)
indices[i] = MOI.ConstraintIndex{F,S}(model.last_constraint_index)
model.affine_constraint_info[model.last_constraint_index] =
ConstraintInfo(length(model.affine_constraint_info) + 1, si, AFFINE)
end
pop!(row_starts)
@checked Lib.XPRSaddrows(
model.inner,
length(rhss),#length(_drhs),
Cint(length(columns)),#Cint(length(_mclind)),
senses,#_srowtype,
rhss,#_drhs,
C_NULL,#_drng,
row_starts .-= 1,#Cint.(_mrstart::Vector{Int}),
columns .-= 1,#Cint.(_mclind::Vector{Int}),
coefficients,#_dmatval
length(rhss), # length(_drhs)
length(columns), # length(_mclind)
senses, # _srowtype
rhss, # _drhs
C_NULL, # _drng
row_starts, # _mrstart
columns, # _mclind
coefficients, # _dmatval
)
return indices
end
Expand Down Expand Up @@ -2655,14 +2644,14 @@ function MOI.add_constraint(
sense, rhs = _sense_and_rhs(is.set)
@checked Lib.XPRSaddrows(
model.inner,
1,#length(_drhs),
Cint(length(indices)),#Cint(length(_mclind)),
Ref{UInt8}(sense),#_srowtype,
Ref(rhs - cte),#_drhs,
C_NULL,#_drng,
Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}),
indices .-= 1,#Cint.(_mrwind::Vector{Int}),
coefficients,#_dmatval
1, # length(_drhs)
length(indices), # length(_mclind)
Ref{UInt8}(sense), # _srowtype
Ref(rhs - cte), # _drhs
C_NULL, # _drng
Ref{Cint}(0), # _mrstart
indices, # _mrwind
coefficients, # _dmatval
)
ncons = @_invoke Lib.XPRSgetintattrib(
model.inner,
Expand All @@ -2676,10 +2665,8 @@ function MOI.add_constraint(
Ref{Cint}(con_value - 1),
Ref{Cint}(indicator_activation(Val{A})),
)
index = MOI.ConstraintIndex{MOI.VectorAffineFunction{T},typeof(is)}(
model.last_constraint_index,
)
return index
F, S = MOI.VectorAffineFunction{T}, typeof(is)
return MOI.ConstraintIndex{F,S}(model.last_constraint_index)
end

function MOI.get(
Expand Down Expand Up @@ -2731,29 +2718,24 @@ function MOI.add_constraint(
f::MOI.ScalarQuadraticFunction{Float64},
s::SCALAR_SETS,
)
F, S = typeof(f), typeof(s)
if !iszero(f.constant)
throw(
MOI.ScalarFunctionConstantNotZero{Float64,typeof(f),typeof(s)}(
f.constant,
),
)
throw(MOI.ScalarFunctionConstantNotZero{Float64,F,S}(f.constant))
end
sense, rhs = _sense_and_rhs(s)
indices, coefficients, I, J, V = _indices_and_coefficients(model, f)
V .*= 0.5 # only for constraints
@checked Lib.XPRSaddrows(
model.inner,
1,
Cint(length(indices)),
length(indices),
Ref{UInt8}(sense),
Ref(rhs),
C_NULL,
Ref{Cint}(0),
indices .-= 1,
indices,
coefficients,
)
V .*= 0.5 # only for constraints
I .-= 1
J .-= 1
ncons = @_invoke Lib.XPRSgetintattrib(
model.inner,
Lib.XPRS_ORIGINALROWS,
Expand All @@ -2770,9 +2752,7 @@ function MOI.add_constraint(
model.last_constraint_index += 1
model.affine_constraint_info[model.last_constraint_index] =
ConstraintInfo(length(model.affine_constraint_info) + 1, s, QUADRATIC)
return MOI.ConstraintIndex{MOI.ScalarQuadraticFunction{Float64},typeof(s)}(
model.last_constraint_index,
)
return MOI.ConstraintIndex{F,S}(model.last_constraint_index)
end

function MOI.get(
Expand Down
22 changes: 22 additions & 0 deletions test/test_MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,28 @@ function test_callback_function_replace()
return
end

function test_add_constraints_scalar_function_constant_not_zero()
model = Xpress.Optimizer()
x = MOI.add_variables(model, 2)
f, s = 1.0 .* x .+ 2.0, MOI.EqualTo.([0.0, 0.0])
@test_throws(
MOI.ScalarFunctionConstantNotZero{Float64,eltype(f),eltype(s)},
MOI.add_constraints(model, f, s),
)
return
end

function test_quadratic_scalar_function_constant_not_zero()
model = Xpress.Optimizer()
x = MOI.add_variable(model)
f, s = 1.0 * x * x + 2.0, MOI.EqualTo(0.0)
@test_throws(
MOI.ScalarFunctionConstantNotZero{Float64,typeof(f),typeof(s)},
MOI.add_constraint(model, f, s),
)
return
end

end # TestMOIWrapper

TestMOIWrapper.runtests()

0 comments on commit 0d0f0dd

Please sign in to comment.