Skip to content

Commit 66a220f

Browse files
authored
[Bridges] add NumberConversionBridge (#2091)
1 parent 19a7f3b commit 66a220f

7 files changed

Lines changed: 360 additions & 14 deletions

File tree

docs/src/submodules/Bridges/list_of_bridges.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Bridges.Constraint.IndicatorLessToGreaterThanBridge
5858
Bridges.Constraint.IndicatorSOS1Bridge
5959
Bridges.Constraint.SemiToBinaryBridge
6060
Bridges.Constraint.ZeroOneBridge
61+
Bridges.Constraint.NumberConversionBridge
6162
Bridges.Constraint.AllDifferentToCountDistinctBridge
6263
Bridges.Constraint.ReifiedAllDifferentToCountDistinctBridge
6364
Bridges.Constraint.BinPackingToMILPBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ include("bridges/ltgt_to_interval.jl")
4242
include("bridges/norm_infinity.jl")
4343
include("bridges/norm_one.jl")
4444
include("bridges/norm_spec_nuc_to_psd.jl")
45+
include("bridges/number_conversion.jl")
4546
include("bridges/quad_to_soc.jl")
4647
include("bridges/relentr_to_exp.jl")
4748
include("bridges/rsoc_soc.jl")
@@ -112,6 +113,8 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
112113
MOI.Bridges.add_bridge(bridged_model, IndicatorGreaterToLessThanBridge{T})
113114
MOI.Bridges.add_bridge(bridged_model, SemiToBinaryBridge{T})
114115
MOI.Bridges.add_bridge(bridged_model, ZeroOneBridge{T})
116+
# Do not add by default
117+
# MOI.Bridges.add_bridge(bridged_model, NumberConversionBridge{T})
115118
# Constraint programming bridges
116119
MOI.Bridges.add_bridge(bridged_model, AllDifferentToCountDistinctBridge{T})
117120
MOI.Bridges.add_bridge(
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
NumberConversionBridge{T,F1,S1,F2,S2} <: Bridges.Constraint.AbstractBridge
9+
10+
`NumberConversionBridge` implements the following reformulation:
11+
12+
* ``f1(x) \\in S1`` to ``f2(x) \\in S2``
13+
14+
where `f` and `S` are the same functional form, but differ in their coefficient
15+
type.
16+
17+
## Source node
18+
19+
`NumberConversionBridge` supports:
20+
21+
* `F1` in `S1`
22+
23+
## Target node
24+
25+
`NumberConversionBridge` creates:
26+
27+
* `F2` in `S2`
28+
"""
29+
struct NumberConversionBridge{T,F1,S1,F2,S2} <: AbstractBridge
30+
constraint::MOI.ConstraintIndex{F2,S2}
31+
end
32+
33+
const NumberConversion{T,OT<:MOI.ModelLike} =
34+
SingleBridgeOptimizer{NumberConversionBridge{T},OT}
35+
36+
function concrete_bridge_type(
37+
::Type{<:NumberConversionBridge{T}},
38+
::Type{F1},
39+
::Type{S1},
40+
) where {T,F1<:MOI.AbstractFunction,S1<:MOI.AbstractSet}
41+
F2 = MOI.Utilities.similar_type(F1, T)
42+
S2 = MOI.Utilities.similar_type(S1, T)
43+
return NumberConversionBridge{T,F1,S1,F2,S2}
44+
end
45+
46+
function bridge_constraint(
47+
::Type{NumberConversionBridge{T,F1,S1,F2,S2}},
48+
model::MOI.ModelLike,
49+
f::F1,
50+
set::S1,
51+
) where {T,F1,S1,F2,S2}
52+
ci = MOI.add_constraint(
53+
model,
54+
MOI.Utilities.convert_approx(F2, f),
55+
MOI.Utilities.convert_approx(S2, set),
56+
)
57+
return NumberConversionBridge{T,F1,S1,F2,S2}(ci)
58+
end
59+
60+
function MOI.supports_constraint(
61+
::Type{NumberConversionBridge{T}},
62+
::Type{F1},
63+
::Type{S1},
64+
) where {T,F1<:MOI.AbstractFunction,S1<:MOI.AbstractSet}
65+
return F1 != MOI.Utilities.similar_type(F1, T) ||
66+
S1 != MOI.Utilities.similar_type(S1, T)
67+
end
68+
69+
function MOI.Bridges.added_constrained_variable_types(
70+
::Type{<:NumberConversionBridge},
71+
)
72+
return Tuple{Type}[]
73+
end
74+
75+
function MOI.Bridges.added_constraint_types(
76+
A::Type{NumberConversionBridge{T,F1,S1,F2,S2}},
77+
) where {T,F1,S1,F2,S2}
78+
return Tuple{Type,Type}[(F2, S2)]
79+
end
80+
81+
function MOI.get(
82+
bridge::NumberConversionBridge{T,F1,S1,F2,S2},
83+
::MOI.NumberOfConstraints{F2,S2},
84+
)::Int64 where {T,F1,S1,F2,S2}
85+
return 1
86+
end
87+
88+
function MOI.get(
89+
bridge::NumberConversionBridge{T,F1,S1,F2,S2},
90+
::MOI.ListOfConstraintIndices{F2,S2},
91+
) where {T,F1,S1,F2,S2}
92+
return [bridge.constraint]
93+
end
94+
95+
function MOI.delete(model::MOI.ModelLike, bridge::NumberConversionBridge)
96+
MOI.delete(model, bridge.constraint)
97+
return
98+
end
99+
100+
MOI.get(b::NumberConversionBridge, ::MOI.NumberOfVariables)::Int64 = 0
101+
102+
function MOI.get(b::NumberConversionBridge, ::MOI.ListOfVariableIndices)
103+
return MOI.VariableIndex[]
104+
end
105+
106+
function MOI.get(
107+
model::MOI.ModelLike,
108+
::MOI.ConstraintFunction,
109+
bridge::NumberConversionBridge{T,F1,S1,F2,S2},
110+
) where {T,F1,S1,F2,S2}
111+
f = MOI.get(model, MOI.ConstraintFunction(), bridge.constraint)
112+
return MOI.Utilities.convert_approx(F1, f)
113+
end
114+
115+
function MOI.get(
116+
model::MOI.ModelLike,
117+
::MOI.ConstraintSet,
118+
bridge::NumberConversionBridge{T,F1,S1,F2,S2},
119+
) where {T,F1,S1,F2,S2}
120+
s = MOI.get(model, MOI.ConstraintSet(), bridge.constraint)
121+
return MOI.Utilities.convert_approx(S1, s)
122+
end

src/Utilities/functions.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,6 +3171,8 @@ tol_default(T::Type{<:AbstractFloat}) = sqrt(eps(T))
31713171

31723172
convert_approx(::Type{T}, func::T; kws...) where {T} = func
31733173

3174+
convert_approx(::Type{F}, func::T; kws...) where {F,T} = convert(F, func)
3175+
31743176
function convert_approx(
31753177
::Type{MOI.VariableIndex},
31763178
func::MOI.ScalarAffineFunction{T};
@@ -3321,6 +3323,8 @@ is_coefficient_type(::Type{<:TypedLike{T}}, ::Type{T}) where {T} = true
33213323

33223324
is_coefficient_type(::Type{<:TypedLike}, ::Type) = false
33233325

3326+
similar_type(::Type{F}, ::Type{T}) where {F,T} = F
3327+
33243328
function similar_type(::Type{<:MOI.ScalarAffineFunction}, ::Type{T}) where {T}
33253329
return MOI.ScalarAffineFunction{T}
33263330
end

src/Utilities/sets.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,32 @@ function trimap(row::Integer, column::Integer)
152152
end
153153
return div((row - 1) * row, 2) + column
154154
end
155+
156+
similar_type(::Type{<:MOI.LessThan}, ::Type{T}) where {T} = MOI.LessThan{T}
157+
158+
function similar_type(::Type{<:MOI.GreaterThan}, ::Type{T}) where {T}
159+
return MOI.GreaterThan{T}
160+
end
161+
162+
similar_type(::Type{<:MOI.EqualTo}, ::Type{T}) where {T} = MOI.EqualTo{T}
163+
164+
similar_type(::Type{<:MOI.Interval}, ::Type{T}) where {T} = MOI.Interval{T}
165+
166+
function convert_approx(::Type{MOI.LessThan{T}}, set::MOI.LessThan) where {T}
167+
return MOI.LessThan{T}(set.upper)
168+
end
169+
170+
function convert_approx(
171+
::Type{MOI.GreaterThan{T}},
172+
set::MOI.GreaterThan,
173+
) where {T}
174+
return MOI.GreaterThan{T}(set.lower)
175+
end
176+
177+
function convert_approx(::Type{MOI.EqualTo{T}}, set::MOI.EqualTo) where {T}
178+
return MOI.EqualTo{T}(set.value)
179+
end
180+
181+
function convert_approx(::Type{MOI.Interval{T}}, set::MOI.Interval) where {T}
182+
return MOI.Interval{T}(set.lower, set.upper)
183+
end

src/functions.jl

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -513,27 +513,13 @@ function Base.convert(
513513
return ScalarAffineFunction{T}(f)
514514
end
515515

516-
function Base.convert(
517-
::Type{ScalarAffineTerm{T}},
518-
t::ScalarAffineTerm{T},
519-
) where {T}
520-
return t
521-
end
522-
523516
function Base.convert(
524517
::Type{ScalarAffineTerm{T}},
525518
t::ScalarAffineTerm,
526519
) where {T}
527520
return ScalarAffineTerm{T}(t.coefficient, t.variable)
528521
end
529522

530-
function Base.convert(
531-
::Type{ScalarAffineFunction{T}},
532-
f::ScalarAffineFunction{T},
533-
) where {T}
534-
return f
535-
end
536-
537523
function Base.convert(
538524
::Type{ScalarAffineFunction{T}},
539525
f::ScalarAffineFunction,
@@ -641,3 +627,66 @@ function Base.convert(
641627
[g.constant],
642628
)
643629
end
630+
631+
function Base.convert(
632+
::Type{ScalarQuadraticTerm{T}},
633+
f::ScalarQuadraticTerm,
634+
) where {T}
635+
return ScalarQuadraticTerm{T}(f.coefficient, f.variable_1, f.variable_2)
636+
end
637+
638+
function Base.convert(
639+
::Type{ScalarQuadraticFunction{T}},
640+
f::ScalarQuadraticFunction,
641+
) where {T}
642+
return ScalarQuadraticFunction{T}(
643+
f.quadratic_terms,
644+
f.affine_terms,
645+
f.constant,
646+
)
647+
end
648+
649+
function Base.convert(
650+
::Type{VectorAffineTerm{T}},
651+
f::VectorAffineTerm,
652+
) where {T}
653+
return VectorAffineTerm{T}(f.output_index, f.scalar_term)
654+
end
655+
656+
function Base.convert(
657+
::Type{VectorAffineFunction{T}},
658+
f::VectorAffineFunction,
659+
) where {T}
660+
return VectorAffineFunction{T}(f.terms, f.constants)
661+
end
662+
663+
function Base.convert(
664+
::Type{VectorQuadraticTerm{T}},
665+
f::VectorQuadraticTerm,
666+
) where {T}
667+
return VectorQuadraticTerm{T}(f.output_index, f.scalar_term)
668+
end
669+
670+
function Base.convert(
671+
::Type{VectorQuadraticFunction{T}},
672+
f::VectorQuadraticFunction,
673+
) where {T}
674+
return VectorQuadraticFunction{T}(
675+
f.quadratic_terms,
676+
f.affine_terms,
677+
f.constants,
678+
)
679+
end
680+
681+
for f in (
682+
:ScalarAffineTerm,
683+
:ScalarAffineFunction,
684+
:ScalarQuadraticTerm,
685+
:ScalarQuadraticFunction,
686+
:VectorAffineTerm,
687+
:VectorAffineFunction,
688+
:VectorQuadraticTerm,
689+
:VectorQuadraticFunction,
690+
)
691+
@eval Base.convert(::Type{$f{T}}, x::$f{T}) where {T} = x
692+
end

0 commit comments

Comments
 (0)