Skip to content

Commit dc98b01

Browse files
authored
Add Parameter set (#2095)
1 parent 8ca9f32 commit dc98b01

10 files changed

Lines changed: 297 additions & 9 deletions

File tree

docs/src/reference/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Integer
6767
ZeroOne
6868
Semicontinuous
6969
Semiinteger
70+
Parameter
7071
```
7172

7273
## Vector sets

src/Bridges/Variable/Variable.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ include("bridges/soc_rsoc.jl")
2121
include("bridges/vectorize.jl")
2222
include("bridges/zeros.jl")
2323
include("bridges/hermitian.jl")
24+
include("bridges/parameter.jl")
2425

2526
"""
2627
add_all_bridges(model, ::Type{T}) where {T}
@@ -38,6 +39,7 @@ function add_all_bridges(model, ::Type{T}) where {T}
3839
MOI.Bridges.add_bridge(model, RSOCtoSOCBridge{T})
3940
MOI.Bridges.add_bridge(model, RSOCtoPSDBridge{T})
4041
MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T})
42+
MOI.Bridges.add_bridge(model, ParameterToEqualToBridge{T})
4143
return
4244
end
4345

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
ParameterToEqualToBridge{T} <: Bridges.Variable.AbstractBridge
9+
10+
`ParameterToEqualToBridge` implements the following reformulation:
11+
12+
* ``x \\in Parameter(v)`` into ``x == v``
13+
14+
## Source node
15+
16+
`ParameterToEqualToBridge` supports:
17+
18+
* [`MOI.VariableIndex`](@ref) in [`MOI.Parameter`](@ref)
19+
20+
## Target nodes
21+
22+
`ParameterToEqualToBridge` creates:
23+
24+
* One variable node: [`MOI.VariableIndex`](@ref) in [`MOI.EqualTo{T}`](@ref)
25+
"""
26+
struct ParameterToEqualToBridge{T} <: AbstractBridge
27+
x::MOI.VariableIndex
28+
ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}
29+
end
30+
31+
const ParameterToEqualTo{T,OT<:MOI.ModelLike} =
32+
SingleBridgeOptimizer{ParameterToEqualToBridge{T},OT}
33+
34+
function bridge_constrained_variable(
35+
::Type{ParameterToEqualToBridge{T}},
36+
model::MOI.ModelLike,
37+
set::MOI.Parameter{T},
38+
) where {T}
39+
x, ci = MOI.add_constrained_variable(model, MOI.EqualTo(set.value))
40+
return ParameterToEqualToBridge{T}(x, ci)
41+
end
42+
43+
function supports_constrained_variable(
44+
::Type{ParameterToEqualToBridge{T}},
45+
::Type{MOI.Parameter{T}},
46+
) where {T}
47+
return true
48+
end
49+
50+
function MOI.Bridges.added_constrained_variable_types(
51+
::Type{ParameterToEqualToBridge{T}},
52+
) where {T}
53+
return Tuple{Type}[(MOI.EqualTo{T},)]
54+
end
55+
56+
function MOI.Bridges.added_constraint_types(::Type{<:ParameterToEqualToBridge})
57+
return Tuple{Type,Type}[]
58+
end
59+
60+
MOI.get(bridge::ParameterToEqualToBridge, ::MOI.NumberOfVariables)::Int64 = 1
61+
62+
function MOI.get(bridge::ParameterToEqualToBridge, ::MOI.ListOfVariableIndices)
63+
return [bridge.x]
64+
end
65+
66+
function MOI.get(
67+
::ParameterToEqualToBridge{T},
68+
::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.EqualTo{T}},
69+
)::Int64 where {T}
70+
return 1
71+
end
72+
73+
function MOI.get(
74+
bridge::ParameterToEqualToBridge{T},
75+
::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.EqualTo{T}},
76+
) where {T}
77+
return [bridge.ci]
78+
end
79+
80+
function MOI.delete(model::MOI.ModelLike, bridge::ParameterToEqualToBridge)
81+
MOI.delete(model, bridge.x)
82+
return
83+
end
84+
85+
function MOI.get(
86+
model::MOI.ModelLike,
87+
::MOI.ConstraintSet,
88+
bridge::ParameterToEqualToBridge{T},
89+
) where {T}
90+
set = MOI.get(model, MOI.ConstraintSet(), bridge.ci)
91+
return MOI.Parameter(set.value)
92+
end
93+
94+
function MOI.get(
95+
model::MOI.ModelLike,
96+
attr::Union{MOI.ConstraintFunction,MOI.ConstraintPrimal,MOI.ConstraintDual},
97+
bridge::ParameterToEqualToBridge,
98+
)
99+
return MOI.get(model, attr, bridge.ci)
100+
end
101+
102+
function _to_one(::Type{T}, x) where {T}
103+
return MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(one(T), x)], zero(T))
104+
end
105+
106+
function MOI.Bridges.bridged_function(
107+
bridge::ParameterToEqualToBridge{T},
108+
) where {T}
109+
return _to_one(T, bridge.x)
110+
end
111+
112+
function unbridged_map(
113+
bridge::ParameterToEqualToBridge{T},
114+
x::MOI.VariableIndex,
115+
) where {T}
116+
return [bridge.x => _to_one(T, x)]
117+
end

src/Test/test_basic_constraint.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ _set(::Any, ::Type{S}) where {S} = _set(S)
7272
_set(::Type{T}, ::Type{MOI.LessThan}) where {T} = MOI.LessThan(one(T))
7373
_set(::Type{T}, ::Type{MOI.GreaterThan}) where {T} = MOI.GreaterThan(one(T))
7474
_set(::Type{T}, ::Type{MOI.EqualTo}) where {T} = MOI.EqualTo(one(T))
75+
_set(::Type{T}, ::Type{MOI.Parameter}) where {T} = MOI.Parameter(one(T))
7576
_set(::Type{T}, ::Type{MOI.Interval}) where {T} = MOI.Interval(zero(T), one(T))
7677
_set(::Type{MOI.ZeroOne}) = MOI.ZeroOne()
7778
_set(::Type{MOI.Integer}) = MOI.Integer()
@@ -264,6 +265,7 @@ for s in [
264265
:GreaterThan,
265266
:LessThan,
266267
:EqualTo,
268+
:Parameter,
267269
:Interval,
268270
:ZeroOne,
269271
:Semicontinuous,

src/Test/test_variable.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,3 +571,29 @@ function test_add_constrained_variables_vector(
571571
@test MOI.get(model, MOI.ConstraintSet(), cv[2]) == sets[2]
572572
return
573573
end
574+
575+
"""
576+
test_add_parameter(model::MOI.ModelLike, config::Config)
577+
578+
Test adding a variable in [`MOI.Parameter`](@ref).
579+
"""
580+
function test_add_parameter(model::MOI.ModelLike, ::Config{T}) where {T}
581+
@requires MOI.supports_add_constrained_variable(model, MOI.Parameter{T})
582+
@test MOI.get(model, MOI.NumberOfVariables()) == 0
583+
x, ci = MOI.add_constrained_variable(model, MOI.Parameter(one(T)))
584+
@test MOI.get(model, MOI.NumberOfVariables()) == 1
585+
@test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.Parameter(one(T))
586+
@test MOI.get(model, MOI.ConstraintFunction(), ci) == x
587+
F, S = MOI.VariableIndex, MOI.Parameter{T}
588+
@test MOI.get(model, MOI.NumberOfConstraints{F,S}()) == 1
589+
@test MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) == [ci]
590+
@test_throws(
591+
MOI.LowerBoundAlreadySet,
592+
MOI.add_constraint(model, x, MOI.GreaterThan(one(T))),
593+
)
594+
@test_throws(
595+
MOI.UpperBoundAlreadySet,
596+
MOI.add_constraint(model, x, MOI.LessThan(one(T))),
597+
)
598+
return
599+
end

src/Utilities/model.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,7 @@ const LessThanIndicatorZero{T} =
770770
MOI.Interval,
771771
MOI.Semicontinuous,
772772
MOI.Semiinteger,
773+
MOI.Parameter,
773774
),
774775
(
775776
MOI.Reals,

src/Utilities/variables_container.jl

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ function set_from_constants(
4747
return S()
4848
end
4949

50+
function set_from_constants(
51+
b::AbstractVectorBounds,
52+
::Type{<:MOI.Parameter},
53+
index,
54+
)
55+
return MOI.Parameter(b.lower[index])
56+
end
57+
5058
"""
5159
SUPPORTED_VARIABLE_SCALAR_SETS{T}
5260
@@ -62,12 +70,13 @@ const SUPPORTED_VARIABLE_SCALAR_SETS{T} = Union{
6270
MOI.ZeroOne,
6371
MOI.Semicontinuous{T},
6472
MOI.Semiinteger{T},
73+
MOI.Parameter{T},
6574
}
6675

67-
# 0xcb = 0x0080 | 0x0040 | 0x0008 | 0x0002 | 0x0001
68-
const _LOWER_BOUND_MASK = 0x00cb
69-
# 0xcd = 0x0080 | 0x0040 | 0x0008 | 0x0004 | 0x0001
70-
const _UPPER_BOUND_MASK = 0x00cd
76+
# 0x01cb = 0x0080 | 0x0040 | 0x0008 | 0x0002 | 0x0001 | 0x0100
77+
const _LOWER_BOUND_MASK = 0x01cb
78+
# 0x01cd = 0x0080 | 0x0040 | 0x0008 | 0x0004 | 0x0001 | 0x0100
79+
const _UPPER_BOUND_MASK = 0x01cd
7180

7281
const _DELETED_VARIABLE = 0x8000
7382

@@ -79,6 +88,7 @@ _single_variable_flag(::Type{MOI.Integer}) = 0x0010
7988
_single_variable_flag(::Type{MOI.ZeroOne}) = 0x0020
8089
_single_variable_flag(::Type{<:MOI.Semicontinuous}) = 0x0040
8190
_single_variable_flag(::Type{<:MOI.Semiinteger}) = 0x0080
91+
_single_variable_flag(::Type{<:MOI.Parameter}) = 0x0100
8292

8393
function _flag_to_set_type(flag::UInt16, ::Type{T}) where {T}
8494
if flag == 0x0001
@@ -95,9 +105,11 @@ function _flag_to_set_type(flag::UInt16, ::Type{T}) where {T}
95105
return MOI.ZeroOne
96106
elseif flag == 0x0040
97107
return MOI.Semicontinuous{T}
98-
else
99-
@assert flag == 0x0080
108+
elseif flag == 0x0080
100109
return MOI.Semiinteger{T}
110+
else
111+
@assert flag == 0x0100
112+
return MOI.Parameter{T}
101113
end
102114
end
103115

@@ -143,15 +155,15 @@ function _lower_bound(
143155
return set.lower
144156
end
145157

146-
_lower_bound(set::MOI.EqualTo) = set.value
158+
_lower_bound(set::Union{MOI.EqualTo,MOI.Parameter}) = set.value
147159

148160
function _upper_bound(
149161
set::Union{MOI.LessThan,MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger},
150162
)
151163
return set.upper
152164
end
153165

154-
_upper_bound(set::MOI.EqualTo) = set.value
166+
_upper_bound(set::Union{MOI.EqualTo,MOI.Parameter}) = set.value
155167

156168
# Use `-Inf` and `Inf` for `AbstractFloat` subtypes.
157169
_no_lower_bound(::Type{T}) where {T} = zero(T)
@@ -373,6 +385,7 @@ function MOI.get(
373385
_add_constraint_type(list, b, MOI.Semiinteger{T})
374386
_add_constraint_type(list, b, MOI.Integer)
375387
_add_constraint_type(list, b, MOI.ZeroOne)
388+
_add_constraint_type(list, b, MOI.Parameter{T})
376389
return list
377390
end
378391

src/sets.jl

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,47 @@ struct EqualTo{T<:Number} <: AbstractScalarSet
236236
value::T
237237
end
238238

239+
"""
240+
Parameter{T<:Number}(value::T)
241+
242+
The set containing the single point ``x \\in \\mathbb{R}`` where ``x`` is given
243+
by `value`.
244+
245+
The `Parameter` set is conceptually similar to the [`EqualTo`](@ref) set, except
246+
that a variable constrained to the `Parameter` set cannot have other constraints
247+
added to it, and the `Parameter` set can never be deleted. Thus, solvers are
248+
free to treat the variable as a constant, and they need not add it as a decision
249+
variable to the model.
250+
251+
Because of this behavior, you must add parameters using [`add_constrained_variable`](@ref),
252+
and solvers should declare [`supports_add_constrained_variable`](@ref) and not
253+
[`supports_constraint`](@ref) for the `Parameter` set.
254+
255+
## Example
256+
257+
```jldoctest
258+
julia> import MathOptInterface as MOI
259+
260+
julia> model = MOI.Utilities.Model{Float64}()
261+
MOIU.Model{Float64}
262+
263+
julia> p, ci = MOI.add_constrained_variable(model, MOI.Parameter(2.5))
264+
(MathOptInterface.VariableIndex(1), MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Parameter{Float64}}(1))
265+
266+
julia> MOI.set(model, MOI.ConstraintSet(), ci, MOI.Parameter(3.0))
267+
268+
julia> MOI.get(model, MOI.ConstraintSet(), ci)
269+
MathOptInterface.Parameter{Float64}(3.0)
270+
```
271+
"""
272+
struct Parameter{T<:Number} <: AbstractScalarSet
273+
value::T
274+
end
275+
239276
function Base.:(==)(
240277
set1::S,
241278
set2::S,
242-
) where {S<:Union{GreaterThan,LessThan,EqualTo}}
279+
) where {S<:Union{GreaterThan,LessThan,EqualTo,Parameter}}
243280
return constant(set1) == constant(set2)
244281
end
245282

@@ -278,6 +315,7 @@ Returns the constant of the set.
278315
constant(s::EqualTo) = s.value
279316
constant(s::GreaterThan) = s.lower
280317
constant(s::LessThan) = s.upper
318+
constant(s::Parameter) = s.value
281319

282320
"""
283321
NormInfinityCone(dimension)
@@ -1875,6 +1913,7 @@ function Base.copy(
18751913
GreaterThan,
18761914
LessThan,
18771915
EqualTo,
1916+
Parameter,
18781917
Interval,
18791918
NormInfinityCone,
18801919
NormOneCone,

0 commit comments

Comments
 (0)