Skip to content

Commit f5a1036

Browse files
authored
Merge pull request #793 from JuliaOpt/bl/delete_vector_of_variables
Clarify deletion of variables
2 parents 63e9f06 + b54ae5d commit f5a1036

File tree

15 files changed

+499
-179
lines changed

15 files changed

+499
-179
lines changed

docs/src/apireference.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ Functions for getting properties of sets.
271271
```@docs
272272
dimension
273273
constant(s::EqualTo)
274+
supports_dimension_update
275+
update_dimension
274276
```
275277

276278
### Scalar sets
@@ -629,6 +631,41 @@ Utilities.state
629631
Utilities.mode
630632
```
631633

634+
## Function utilities
635+
636+
The following utilities are available for functions:
637+
```@docs
638+
Utilities.eval_variables
639+
Utilities.remove_variable
640+
Utilities.all_coefficients
641+
Utilities.unsafe_add
642+
Utilities.isapprox_zero
643+
Utilities.modify_function
644+
```
645+
646+
The following functions can be used to canonicalize a function:
647+
```@docs
648+
Utilities.is_canonical
649+
Utilities.canonical
650+
Utilities.canonicalize!
651+
```
652+
653+
The following functions can be used to manipulate functions with basic algebra:
654+
```@docs
655+
Utilities.scalar_type
656+
Utilities.promote_operation
657+
Utilities.operate
658+
Utilities.operate!
659+
Utilities.vectorize
660+
```
661+
662+
## Set utilities
663+
664+
The following utilities are available for sets:
665+
```@docs
666+
Utilities.shift_constant
667+
```
668+
632669
## Benchmarks
633670

634671
Functions to help benchmark the performance of solver wrappers. See

src/Bridges/Constraint/slack.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ end
9393

9494
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
9595
b::ScalarSlackBridge{T}) where T
96-
return MOIU.removevariable(MOI.get(model, attr, b.equality), b.slack)
96+
return MOIU.remove_variable(MOI.get(model, attr, b.equality), b.slack)
9797
end
9898
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
9999
b::ScalarSlackBridge)
@@ -191,7 +191,7 @@ end
191191

192192
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
193193
b::VectorSlackBridge{T}) where T
194-
return MOIU.removevariable(MOI.get(model, attr, b.equality), b.slacks)
194+
return MOIU.remove_variable(MOI.get(model, attr, b.equality), b.slacks)
195195
end
196196
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
197197
b::VectorSlackBridge)

src/Test/modellike.jl

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,3 +471,95 @@ function set_upper_bound_twice(model::MOI.ModelLike, T::Type)
471471
MOI.delete(model, ci)
472472
end
473473
end
474+
475+
function delete_test(model::MOI.ModelLike)
476+
x = MOI.add_variable(model)
477+
cx = MOI.add_constraint(model, x, MOI.GreaterThan(0.0))
478+
y = MOI.add_variables(model, 4)
479+
cy = MOI.add_constraint(model, y, MOI.Nonpositives(4))
480+
@test MOI.is_valid(model, x)
481+
@test MOI.is_valid(model, y[1])
482+
@test MOI.is_valid(model, y[2])
483+
@test MOI.is_valid(model, y[3])
484+
@test MOI.is_valid(model, y[4])
485+
@test MOI.is_valid(model, cx)
486+
@test MOI.is_valid(model, cy)
487+
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
488+
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
489+
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y)
490+
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(4)
491+
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
492+
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
493+
(MOI.VectorOfVariables, MOI.Nonpositives)])
494+
@test MOI.get(model, MOI.ListOfConstraintIndices{
495+
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
496+
@test MOI.get(model, MOI.ListOfConstraintIndices{
497+
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
498+
MOI.delete(model, y[3])
499+
@test MOI.is_valid(model, x)
500+
@test MOI.is_valid(model, y[1])
501+
@test MOI.is_valid(model, y[2])
502+
@test !MOI.is_valid(model, y[3])
503+
@test MOI.is_valid(model, y[4])
504+
@test MOI.is_valid(model, cx)
505+
@test MOI.is_valid(model, cy)
506+
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
507+
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
508+
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[1, 2, 4]])
509+
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(3)
510+
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
511+
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
512+
(MOI.VectorOfVariables, MOI.Nonpositives)])
513+
@test MOI.get(model, MOI.ListOfConstraintIndices{
514+
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
515+
@test MOI.get(model, MOI.ListOfConstraintIndices{
516+
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
517+
MOI.delete(model, y[1])
518+
@test MOI.is_valid(model, x)
519+
@test !MOI.is_valid(model, y[1])
520+
@test MOI.is_valid(model, y[2])
521+
@test !MOI.is_valid(model, y[3])
522+
@test MOI.is_valid(model, y[4])
523+
@test MOI.is_valid(model, cx)
524+
@test MOI.is_valid(model, cy)
525+
@test MOI.get(model, MOI.ConstraintFunction(), cx) == MOI.SingleVariable(x)
526+
@test MOI.get(model, MOI.ConstraintSet(), cx) == MOI.GreaterThan(0.0)
527+
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[2, 4]])
528+
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(2)
529+
@test Set(MOI.get(model, MOI.ListOfConstraints())) ==
530+
Set([(MOI.SingleVariable, MOI.GreaterThan{Float64}),
531+
(MOI.VectorOfVariables, MOI.Nonpositives)])
532+
@test MOI.get(model, MOI.ListOfConstraintIndices{
533+
MOI.SingleVariable, MOI.GreaterThan{Float64}}()) == [cx]
534+
@test MOI.get(model, MOI.ListOfConstraintIndices{
535+
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
536+
MOI.delete(model, x)
537+
@test !MOI.is_valid(model, x)
538+
@test !MOI.is_valid(model, y[1])
539+
@test MOI.is_valid(model, y[2])
540+
@test !MOI.is_valid(model, y[3])
541+
@test MOI.is_valid(model, y[4])
542+
@test !MOI.is_valid(model, cx)
543+
@test MOI.is_valid(model, cy)
544+
@test MOI.get(model, MOI.ConstraintFunction(), cy) == MOI.VectorOfVariables(y[[2, 4]])
545+
@test MOI.get(model, MOI.ConstraintSet(), cy) == MOI.Nonpositives(2)
546+
@test MOI.get(model, MOI.ListOfConstraints()) ==
547+
[(MOI.VectorOfVariables, MOI.Nonpositives)]
548+
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
549+
MOI.SingleVariable, MOI.GreaterThan{Float64}}()))
550+
@test MOI.get(model, MOI.ListOfConstraintIndices{
551+
MOI.VectorOfVariables, MOI.Nonpositives}()) == [cy]
552+
MOI.delete(model, y[[2, 4]])
553+
@test !MOI.is_valid(model, x)
554+
@test !MOI.is_valid(model, y[1])
555+
@test !MOI.is_valid(model, y[2])
556+
@test !MOI.is_valid(model, y[3])
557+
@test !MOI.is_valid(model, y[4])
558+
@test !MOI.is_valid(model, cx)
559+
@test !MOI.is_valid(model, cy)
560+
@test isempty(MOI.get(model, MOI.ListOfConstraints()))
561+
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
562+
MOI.SingleVariable, MOI.GreaterThan{Float64}}()))
563+
@test isempty(MOI.get(model, MOI.ListOfConstraintIndices{
564+
MOI.VectorOfVariables, MOI.Nonpositives}()))
565+
end

src/Utilities/functions.jl

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
using Test
22

33
"""
4-
evalvariables(varval::Function, f::AbstractFunction)
4+
eval_variables(varval::Function, f::AbstractFunction)
55
66
Returns the value of function `f` if each variable index `vi` is evaluated as `varval(vi)`.
77
"""
8-
function evalvariables end
9-
evalvariables(varval::Function, f::SVF) = varval(f.variable)
10-
evalvariables(varval::Function, f::VVF) = varval.(f.variables)
11-
function evalvariables(varval::Function, f::SAF)
8+
function eval_variables end
9+
eval_variables(varval::Function, f::SVF) = varval(f.variable)
10+
eval_variables(varval::Function, f::VVF) = varval.(f.variables)
11+
function eval_variables(varval::Function, f::SAF)
1212
return mapreduce(t->evalterm(varval, t), +, f.terms, init=f.constant)
1313
end
14-
function evalvariables(varval::Function, f::VAF)
14+
function eval_variables(varval::Function, f::VAF)
1515
out = copy(f.constants)
1616
for t in f.terms
1717
out[t.output_index] += evalterm(varval, t.scalar_term)
1818
end
1919
out
2020
end
21-
function evalvariables(varval::Function, f::SQF)
21+
function eval_variables(varval::Function, f::SQF)
2222
init = zero(f.constant)
2323
lin = mapreduce(t->evalterm(varval, t), +, f.affine_terms, init=init)
2424
quad = mapreduce(t->evalterm(varval, t), +, f.quadratic_terms, init=init)
2525
return lin + quad + f.constant
2626
end
27-
function evalvariables(varval::Function, f::VQF)
27+
function eval_variables(varval::Function, f::VQF)
2828
out = copy(f.constants)
2929
for t in f.affine_terms
3030
out[t.output_index] += evalterm(varval, t.scalar_term)
@@ -204,13 +204,13 @@ function unsafe_add(t1::VT, t2::VT) where VT <: Union{MOI.VectorAffineTerm,
204204
end
205205

206206
"""
207-
iscanonical(f::Union{ScalarAffineFunction, ScalarQuadraticFunction
207+
is_canonical(f::Union{ScalarAffineFunction, ScalarQuadraticFunction
208208
VectorAffineFunction, VectorQuadraticTerm})
209209
210210
Returns a Bool indicating whether the function is in canonical form.
211211
See [`canonical`](@ref).
212212
"""
213-
function iscanonical(f::Union{SAF, VAF, SQF, VQF})
213+
function is_canonical(f::Union{SAF, VAF, SQF, VQF})
214214
is_strictly_sorted(f.terms, MOI.term_indices,
215215
t -> !iszero(MOI.coefficient(t)))
216216
end
@@ -460,44 +460,44 @@ function _rmvar(vis_or_terms::Vector, vi)
460460
end
461461

462462
"""
463-
removevariable(f::AbstractFunction, vi::VariableIndex)
463+
remove_variable(f::AbstractFunction, vi::VariableIndex)
464464
465465
Return a new function `f` with the variable vi removed.
466466
"""
467-
function removevariable end
468-
function removevariable(f::MOI.SingleVariable, vi::MOI.VariableIndex)
467+
function remove_variable end
468+
function remove_variable(f::MOI.SingleVariable, vi::MOI.VariableIndex)
469469
if f.variable == vi
470470
error("Cannot remove variable from a `SingleVariable` function of the",
471471
" same variable.")
472472
end
473473
return f
474474
end
475-
function removevariable(f::VVF, vi)
475+
function remove_variable(f::VVF, vi)
476476
VVF(_rmvar(f.variables, vi))
477477
end
478-
function removevariable(f::Union{SAF, VAF}, vi)
478+
function remove_variable(f::Union{SAF, VAF}, vi)
479479
typeof(f)(_rmvar(f.terms, vi), MOI.constant(f))
480480
end
481-
function removevariable(f::Union{SQF, VQF}, vi)
481+
function remove_variable(f::Union{SQF, VQF}, vi)
482482
terms = _rmvar.((f.affine_terms, f.quadratic_terms), Ref(vi))
483483
typeof(f)(terms..., MOI.constant(f))
484484
end
485485

486486
"""
487-
modifyfunction(f::AbstractFunction, change::AbstractFunctionModification)
487+
modify_function(f::AbstractFunction, change::AbstractFunctionModification)
488488
489489
Return a new function `f` modified according to `change`.
490490
"""
491-
function modifyfunction(f::SAF, change::MOI.ScalarConstantChange)
491+
function modify_function(f::SAF, change::MOI.ScalarConstantChange)
492492
return SAF(f.terms, change.new_constant)
493493
end
494-
function modifyfunction(f::VAF, change::MOI.VectorConstantChange)
494+
function modify_function(f::VAF, change::MOI.VectorConstantChange)
495495
return VAF(f.terms, change.new_constant)
496496
end
497-
function modifyfunction(f::SQF, change::MOI.ScalarConstantChange)
497+
function modify_function(f::SQF, change::MOI.ScalarConstantChange)
498498
return SQF(f.affine_terms, f.quadratic_terms, change.new_constant)
499499
end
500-
function modifyfunction(f::VQF, change::MOI.VectorConstantChange)
500+
function modify_function(f::VQF, change::MOI.VectorConstantChange)
501501
return VQF(f.affine_terms, f.quadratic_terms, change.new_constant)
502502
end
503503
function _modifycoefficient(terms::Vector{<:MOI.ScalarAffineTerm}, variable::VI, new_coefficient)
@@ -518,11 +518,11 @@ function _modifycoefficient(terms::Vector{<:MOI.ScalarAffineTerm}, variable::VI,
518518
end
519519
terms
520520
end
521-
function modifyfunction(f::SAF, change::MOI.ScalarCoefficientChange)
521+
function modify_function(f::SAF, change::MOI.ScalarCoefficientChange)
522522
lin = _modifycoefficient(f.terms, change.variable, change.new_coefficient)
523523
return SAF(lin, f.constant)
524524
end
525-
function modifyfunction(f::SQF, change::MOI.ScalarCoefficientChange)
525+
function modify_function(f::SQF, change::MOI.ScalarCoefficientChange)
526526
lin = _modifycoefficient(f.affine_terms, change.variable, change.new_coefficient)
527527
return SQF(lin, f.quadratic_terms, f.constant)
528528
end
@@ -553,13 +553,13 @@ function _modifycoefficients(n, terms::Vector{<:MOI.VectorAffineTerm}, variable:
553553
end
554554
terms
555555
end
556-
function modifyfunction(f::VAF, change::MOI.MultirowChange)
556+
function modify_function(f::VAF, change::MOI.MultirowChange)
557557
dim = MOI.output_dimension(f)
558558
coefficients = change.new_coefficients
559559
lin = _modifycoefficients(dim, f.terms, change.variable, coefficients)
560560
VAF(lin, f.constants)
561561
end
562-
function modifyfunction(f::VQF, change::MOI.MultirowChange)
562+
function modify_function(f::VQF, change::MOI.MultirowChange)
563563
dim = MOI.output_dimension(f)
564564
coefficients = change.new_coefficients
565565
lin = _modifycoefficients(dim, f.affine_terms, change.variable, coefficients)

src/Utilities/mockoptimizer.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,19 @@ function MOI.delete(mock::MockOptimizer, index::MOI.VariableIndex)
385385
MOI.delete(mock.inner_model, xor_index(index))
386386
delete!(mock.varprimal, index)
387387
end
388+
function MOI.delete(mock::MockOptimizer, indices::Vector{MOI.VariableIndex})
389+
if !mock.delete_allowed && !isempty(indices)
390+
throw(MOI.DeleteNotAllowed(first(indices)))
391+
end
392+
for index in indices
393+
# The index thrown by `mock.inner_model` would be xored
394+
MOI.throw_if_not_valid(mock, index)
395+
end
396+
MOI.delete(mock.inner_model, xor_index.(indices))
397+
for index in indices
398+
delete!(mock.varprimal, index)
399+
end
400+
end
388401
function MOI.delete(mock::MockOptimizer, index::MOI.ConstraintIndex)
389402
if !mock.delete_allowed
390403
throw(MOI.DeleteNotAllowed(index))

0 commit comments

Comments
 (0)