Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add batched modification methods #3716

Merged
merged 22 commits into from
Mar 28, 2024
23 changes: 21 additions & 2 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ function add_constraint(
end

"""
set_normalized_rhs(constraint::ConstraintRef, value)
set_normalized_rhs(constraint::ConstraintRef, value::Number)

Set the right-hand side term of `constraint` to `value`.

Expand All @@ -758,7 +758,7 @@ con : 2 x ≤ 4
"""
function set_normalized_rhs(
con_ref::ConstraintRef{<:AbstractModel,MOI.ConstraintIndex{F,S}},
value,
value::Number,
) where {
T,
S<:Union{MOI.LessThan{T},MOI.GreaterThan{T},MOI.EqualTo{T}},
Expand All @@ -773,6 +773,25 @@ function set_normalized_rhs(
return
end

function set_normalized_rhs(
odow marked this conversation as resolved.
Show resolved Hide resolved
constraints::AbstractVector{
<:ConstraintRef{<:AbstractModel,MOI.ConstraintIndex{F,S}},
},
values::AbstractVector{<:Number},
) where {
T,
S<:Union{MOI.LessThan{T},MOI.GreaterThan{T},MOI.EqualTo{T}},
F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}},
}
MOI.set(
backend(owner_model(first(constraints))),
MOI.ConstraintSet(),
index.(constraints),
S.(convert.(T, values)),
)
return
end

"""
normalized_rhs(constraint::ConstraintRef)

Expand Down
195 changes: 194 additions & 1 deletion src/objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ julia> set_objective_coefficient(model, x, 3)
julia> objective_function(model)
3 x + 1
```

See also [`set_objective_coefficients`](@ref).
"""
function set_objective_coefficient(
model::GenericModel{T},
Expand Down Expand Up @@ -491,11 +493,103 @@ function _set_objective_coefficient(
return
end

"""
set_objective_coefficients(
odow marked this conversation as resolved.
Show resolved Hide resolved
model::GenericModel,
variables::Vector{<:GenericVariableRef},
coefficients::Vector{<:Real},
)

Set multiple linear objective coefficients associated with `variables` to `coefficients`, in a single call.

Note: this function will throw an error if a nonlinear objective is set.

## Example

```jldoctest
julia> model = Model();

julia> @variable(model, x);

julia> @variable(model, y);

julia> @objective(model, Min, 3x + 2y + 1)
3 x + 2 y + 1

julia> set_objective_coefficients(model, [x, y], [5, 4])

julia> objective_function(model)
5 x + 4 y + 1
```

See also [`set_objective_coefficient`](@ref).
"""
function set_objective_coefficients(
model::GenericModel{T},
variables::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:Real},
) where {T}
if _nlp_objective_function(model) !== nothing
error("A nonlinear objective is already set in the model")
odow marked this conversation as resolved.
Show resolved Hide resolved
end
if length(variables) != length(coeffs)
throw(
DimensionMismatch(
"The number of variables and coefficients must match",
),
)
end
coeffs_t = convert.(T, coeffs)::AbstractVector{T}
F = objective_function_type(model)
_set_objective_coefficients(model, variables, coeffs_t, F)
model.is_model_dirty = true
return
end

function _set_objective_coefficients(
model::GenericModel{T},
variables::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:T},
::Type{GenericVariableRef{T}},
) where {T}
current_obj = objective_function(model)
current_obj_index = index(current_obj)
if length(variables) > 0
position = findfirst(x -> index(x) == current_obj_index, variables)
if position === nothing
set_objective_function(
model,
add_to_expression!(
LinearAlgebra.dot(coeffs, variables),
current_obj,
),
)
else
set_objective_function(model, LinearAlgebra.dot(coeffs, variables))
end
end
return
end

function _set_objective_coefficients(
model::GenericModel{T},
variables::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:T},
::Type{F},
) where {T,F}
MOI.modify(
backend(model),
MOI.ObjectiveFunction{moi_function_type(F)}(),
MOI.ScalarCoefficientChange.(index.(variables), coeffs),
)
return
end

"""
set_objective_coefficient(
model::GenericModel{T},
variable_1::GenericVariableRef{T},
variable_1::GenericVariableRef{T},
variable_2::GenericVariableRef{T},
coefficient::Real,
) where {T}

Expand All @@ -521,6 +615,8 @@ julia> set_objective_coefficient(model, x[1], x[2], 3)
julia> objective_function(model)
2 x[1]² + 3 x[1]*x[2]
```

See also [`set_objective_coefficients`](@ref).
"""
function set_objective_coefficient(
model::GenericModel{T},
Expand Down Expand Up @@ -572,3 +668,100 @@ function _set_objective_coefficient(
)
return
end

"""
set_objective_coefficients(
model::GenericModel{T},
variables_1::AbstractVector{<GenericVariableRef{T}},
variables_2::AbstractVector{<GenericVariableRef{T}},
coefficients::AbstractVector{<Real},
) where {T}

Set multiple quadratic objective coefficients associated with `variables_1` and
`variables_2` to `coefficients`, in a single call.

Note: this function will throw an error if a nonlinear objective is set.

## Example

```jldoctest
julia> model = Model();

julia> @variable(model, x[1:2]);

julia> @objective(model, Min, x[1]^2 + x[1] * x[2])
x[1]² + x[1]*x[2]

julia> set_objective_coefficients(model, [x[1], x[1]], [x[1], x[2]], [2, 3])

julia> objective_function(model)
2 x[1]² + 3 x[1]*x[2]
```

See also [`set_objective_coefficient`](@ref).
"""
function set_objective_coefficients(
model::GenericModel{T},
variables_1::AbstractVector{<:GenericVariableRef{T}},
variables_2::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:Real},
) where {T}
if _nlp_objective_function(model) !== nothing
error("A nonlinear objective is already set in the model")
end
if !(length(variables_1) == length(variables_2) == length(coeffs))
throw(
DimensionMismatch(
"The number of variables and coefficients must match",
),
)
end
coeffs_t = convert.(T, coeffs)::AbstractVector{<:T}
odow marked this conversation as resolved.
Show resolved Hide resolved
F = moi_function_type(objective_function_type(model))
_set_objective_coefficients(model, variables_1, variables_2, coeffs_t, F)
model.is_model_dirty = true
return
end

function _set_objective_coefficients(
model::GenericModel{T},
variables_1::AbstractVector{<:GenericVariableRef{T}},
variables_2::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:T},
::Type{F},
) where {T,F}
current_obj = objective_function(model)
new_obj = add_to_expression!(
sum(
coeffs[i] * variables_1[i] * variables_2[i] for
i in eachindex(coeffs)
),
current_obj,
)
set_objective_function(model, new_obj)
return
end

function _set_objective_coefficients(
model::GenericModel{T},
variables_1::AbstractVector{<:GenericVariableRef{T}},
variables_2::AbstractVector{<:GenericVariableRef{T}},
coeffs::AbstractVector{<:T},
::Type{MOI.ScalarQuadraticFunction{T}},
) where {T}
for i in eachindex(variables_1)
if variables_1[i] == variables_2[i]
coeffs[i] *= T(2)
end
end
MOI.modify(
backend(model),
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(),
MOI.ScalarQuadraticCoefficientChange.(
index.(variables_1),
index.(variables_2),
coeffs,
),
)
return
end
Loading
Loading