Skip to content

Clarify deletion of variables #793

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

Merged
merged 12 commits into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ Functions for getting properties of sets.
```@docs
dimension
constant(s::EqualTo)
supports_dimension_update
update_dimension
```

### Scalar sets
Expand Down Expand Up @@ -612,6 +614,41 @@ Utilities.state
Utilities.mode
```

## Function utilities

The following utilities are available for functions:
```@docs
Utilities.eval_variables
Utilities.remove_variable
Utilities.all_coefficients
Utilities.unsafe_add
Utilities.isapprox_zero
Utilities.modify_function
```

The following functions can be used to canonicalize a function:
```@docs
Utilities.is_canonical
Utilities.canonical
Utilities.canonicalize!
```

The following functions can be used to manipulate functions with basic algebra:
```@docs
Utilities.scalar_type
Utilities.promote_operation
Utilities.operate
Utilities.operate!
Utilities.vectorize
```

## Set utilities

The following utilities are available for sets:
```@docs
Utilities.shift_constant
```

## Benchmarks

Functions to help benchmark the performance of solver wrappers. See
Expand Down
4 changes: 2 additions & 2 deletions src/Bridges/Constraint/slack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
b::ScalarSlackBridge{T}) where T
return MOIU.removevariable(MOI.get(model, attr, b.equality), b.slack)
return MOIU.remove_variable(MOI.get(model, attr, b.equality), b.slack)
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
b::ScalarSlackBridge)
Expand Down Expand Up @@ -191,7 +191,7 @@ end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
b::VectorSlackBridge{T}) where T
return MOIU.removevariable(MOI.get(model, attr, b.equality), b.slacks)
return MOIU.remove_variable(MOI.get(model, attr, b.equality), b.slacks)
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
b::VectorSlackBridge)
Expand Down
92 changes: 92 additions & 0 deletions src/Test/modellike.jl
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,95 @@ function set_upper_bound_twice(model::MOI.ModelLike, T::Type)
MOI.delete(model, ci)
end
end

function delete_test(model::MOI.ModelLike)
x = MOI.add_variable(model)
cx = MOI.add_constraint(model, x, MOI.GreaterThan(0.0))
y = MOI.add_variables(model, 4)
cy = MOI.add_constraint(model, y, MOI.Nonpositives(4))
@test MOI.is_valid(model, x)
@test MOI.is_valid(model, y[1])
@test MOI.is_valid(model, y[2])
@test MOI.is_valid(model, y[3])
@test MOI.is_valid(model, y[4])
@test MOI.is_valid(model, cx)
@test MOI.is_valid(model, cy)
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y)
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(4)
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
(MOI.VectorOfVariables, MOI.Nonpositives)])
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
MOI.delete(model, y[3])
@test MOI.is_valid(model, x)
@test MOI.is_valid(model, y[1])
@test MOI.is_valid(model, y[2])
@test !MOI.is_valid(model, y[3])
@test MOI.is_valid(model, y[4])
@test MOI.is_valid(model, cx)
@test MOI.is_valid(model, cy)
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[1, 2, 4]])
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(3)
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
(MOI.VectorOfVariables, MOI.Nonpositives)])
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
MOI.delete(model, y[1])
@test MOI.is_valid(model, x)
@test !MOI.is_valid(model, y[1])
@test MOI.is_valid(model, y[2])
@test !MOI.is_valid(model, y[3])
@test MOI.is_valid(model, y[4])
@test MOI.is_valid(model, cx)
@test MOI.is_valid(model, cy)
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[2, 4]])
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(2)
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
(MOI.VectorOfVariables, MOI.Nonpositives)])
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
MOI.delete(model, x)
@test !MOI.is_valid(model, x)
@test !MOI.is_valid(model, y[1])
@test MOI.is_valid(model, y[2])
@test !MOI.is_valid(model, y[3])
@test MOI.is_valid(model, y[4])
@test !MOI.is_valid(model, cx)
@test MOI.is_valid(model, cy)
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[2, 4]])
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(2)
@test MOI.get(model, MOI.ListOfConstraints()) ==
[(MOI.VectorOfVariables, MOI.Nonpositives)]
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
MOI.SingleVariable, MOI.GreaterThan{Float64}}()))
@test MOI.get(model, MOI.ListOfConstraintIndices{
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
MOI.delete(model, y[[2, 4]])
@test !MOI.is_valid(model, x)
@test !MOI.is_valid(model, y[1])
@test !MOI.is_valid(model, y[2])
@test !MOI.is_valid(model, y[3])
@test !MOI.is_valid(model, y[4])
@test !MOI.is_valid(model, cx)
@test !MOI.is_valid(model, cy)
@test isempty(MOI.get(model, MOI.ListOfConstraints()))
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
MOI.SingleVariable, MOI.GreaterThan{Float64}}()))
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
MOI.VectorOfVariables, MOI.Nonpositives}()))
end
50 changes: 25 additions & 25 deletions src/Utilities/functions.jl
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
using Test

"""
evalvariables(varval::Function, f::AbstractFunction)
eval_variables(varval::Function, f::AbstractFunction)

Returns the value of function `f` if each variable index `vi` is evaluated as `varval(vi)`.
"""
function evalvariables end
evalvariables(varval::Function, f::SVF) = varval(f.variable)
evalvariables(varval::Function, f::VVF) = varval.(f.variables)
function evalvariables(varval::Function, f::SAF)
function eval_variables end
eval_variables(varval::Function, f::SVF) = varval(f.variable)
eval_variables(varval::Function, f::VVF) = varval.(f.variables)
function eval_variables(varval::Function, f::SAF)
return mapreduce(t->evalterm(varval, t), +, f.terms, init=f.constant)
end
function evalvariables(varval::Function, f::VAF)
function eval_variables(varval::Function, f::VAF)
out = copy(f.constants)
for t in f.terms
out[t.output_index] += evalterm(varval, t.scalar_term)
end
out
end
function evalvariables(varval::Function, f::SQF)
function eval_variables(varval::Function, f::SQF)
init = zero(f.constant)
lin = mapreduce(t->evalterm(varval, t), +, f.affine_terms, init=init)
quad = mapreduce(t->evalterm(varval, t), +, f.quadratic_terms, init=init)
return lin + quad + f.constant
end
function evalvariables(varval::Function, f::VQF)
function eval_variables(varval::Function, f::VQF)
out = copy(f.constants)
for t in f.affine_terms
out[t.output_index] += evalterm(varval, t.scalar_term)
Expand Down Expand Up @@ -204,13 +204,13 @@ function unsafe_add(t1::VT, t2::VT) where VT <: Union{MOI.VectorAffineTerm,
end

"""
iscanonical(f::Union{ScalarAffineFunction, ScalarQuadraticFunction
is_canonical(f::Union{ScalarAffineFunction, ScalarQuadraticFunction
VectorAffineFunction, VectorQuadraticTerm})

Returns a Bool indicating whether the function is in canonical form.
See [`canonical`](@ref).
"""
function iscanonical(f::Union{SAF, VAF, SQF, VQF})
function is_canonical(f::Union{SAF, VAF, SQF, VQF})
is_strictly_sorted(f.terms, MOI.term_indices,
t -> !iszero(MOI.coefficient(t)))
end
Expand Down Expand Up @@ -460,44 +460,44 @@ function _rmvar(vis_or_terms::Vector, vi)
end

"""
removevariable(f::AbstractFunction, vi::VariableIndex)
remove_variable(f::AbstractFunction, vi::VariableIndex)

Return a new function `f` with the variable vi removed.
"""
function removevariable end
function removevariable(f::MOI.SingleVariable, vi::MOI.VariableIndex)
function remove_variable end
function remove_variable(f::MOI.SingleVariable, vi::MOI.VariableIndex)
if f.variable == vi
error("Cannot remove variable from a `SingleVariable` function of the",
" same variable.")
end
return f
end
function removevariable(f::VVF, vi)
function remove_variable(f::VVF, vi)
VVF(_rmvar(f.variables, vi))
end
function removevariable(f::Union{SAF, VAF}, vi)
function remove_variable(f::Union{SAF, VAF}, vi)
typeof(f)(_rmvar(f.terms, vi), MOI.constant(f))
end
function removevariable(f::Union{SQF, VQF}, vi)
function remove_variable(f::Union{SQF, VQF}, vi)
terms = _rmvar.((f.affine_terms, f.quadratic_terms), Ref(vi))
typeof(f)(terms..., MOI.constant(f))
end

"""
modifyfunction(f::AbstractFunction, change::AbstractFunctionModification)
modify_function(f::AbstractFunction, change::AbstractFunctionModification)

Return a new function `f` modified according to `change`.
"""
function modifyfunction(f::SAF, change::MOI.ScalarConstantChange)
function modify_function(f::SAF, change::MOI.ScalarConstantChange)
return SAF(f.terms, change.new_constant)
end
function modifyfunction(f::VAF, change::MOI.VectorConstantChange)
function modify_function(f::VAF, change::MOI.VectorConstantChange)
return VAF(f.terms, change.new_constant)
end
function modifyfunction(f::SQF, change::MOI.ScalarConstantChange)
function modify_function(f::SQF, change::MOI.ScalarConstantChange)
return SQF(f.affine_terms, f.quadratic_terms, change.new_constant)
end
function modifyfunction(f::VQF, change::MOI.VectorConstantChange)
function modify_function(f::VQF, change::MOI.VectorConstantChange)
return VQF(f.affine_terms, f.quadratic_terms, change.new_constant)
end
function _modifycoefficient(terms::Vector{<:MOI.ScalarAffineTerm}, variable::VI, new_coefficient)
Expand All @@ -518,11 +518,11 @@ function _modifycoefficient(terms::Vector{<:MOI.ScalarAffineTerm}, variable::VI,
end
terms
end
function modifyfunction(f::SAF, change::MOI.ScalarCoefficientChange)
function modify_function(f::SAF, change::MOI.ScalarCoefficientChange)
lin = _modifycoefficient(f.terms, change.variable, change.new_coefficient)
return SAF(lin, f.constant)
end
function modifyfunction(f::SQF, change::MOI.ScalarCoefficientChange)
function modify_function(f::SQF, change::MOI.ScalarCoefficientChange)
lin = _modifycoefficient(f.affine_terms, change.variable, change.new_coefficient)
return SQF(lin, f.quadratic_terms, f.constant)
end
Expand Down Expand Up @@ -553,13 +553,13 @@ function _modifycoefficients(n, terms::Vector{<:MOI.VectorAffineTerm}, variable:
end
terms
end
function modifyfunction(f::VAF, change::MOI.MultirowChange)
function modify_function(f::VAF, change::MOI.MultirowChange)
dim = MOI.output_dimension(f)
coefficients = change.new_coefficients
lin = _modifycoefficients(dim, f.terms, change.variable, coefficients)
VAF(lin, f.constants)
end
function modifyfunction(f::VQF, change::MOI.MultirowChange)
function modify_function(f::VQF, change::MOI.MultirowChange)
dim = MOI.output_dimension(f)
coefficients = change.new_coefficients
lin = _modifycoefficients(dim, f.affine_terms, change.variable, coefficients)
Expand Down
13 changes: 13 additions & 0 deletions src/Utilities/mockoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,19 @@ function MOI.delete(mock::MockOptimizer, index::MOI.VariableIndex)
MOI.delete(mock.inner_model, xor_index(index))
delete!(mock.varprimal, index)
end
function MOI.delete(mock::MockOptimizer, indices::Vector{MOI.VariableIndex})
if !mock.delete_allowed && !isempty(indices)
throw(MOI.DeleteNotAllowed(first(indices)))
end
for index in indices
# The index thrown by `mock.inner_model` would be xored
MOI.throw_if_not_valid(mock, index)
end
MOI.delete(mock.inner_model, xor_index.(indices))
for index in indices
delete!(mock.varprimal, index)
end
end
function MOI.delete(mock::MockOptimizer, index::MOI.ConstraintIndex)
if !mock.delete_allowed
throw(MOI.DeleteNotAllowed(index))
Expand Down
Loading