Skip to content

Commit d46d940

Browse files
committed
Fixes
1 parent c6ecc1d commit d46d940

4 files changed

Lines changed: 127 additions & 258 deletions

File tree

src/MatrixOptInterface.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ end
6565

6666
@enum VariableType CONTINUOUS INTEGER BINARY
6767

68+
include("product_of_sets.jl")
6869
include("conic_form.jl")
6970
include("matrix_input.jl")
7071
#include("change_form.jl")

src/conic_form.jl

Lines changed: 27 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
55

66
"""
7-
GeometricConicForm{T, AT, VT, C} <: MOI.ModelLike
7+
empty_geometric_conic_form
88
99
Represents an optimization model of the form:
1010
```
@@ -13,246 +13,37 @@ s.t. b_i - A_i x ∈ C_i ∀ i
1313
```
1414
with each `C_i` a cone defined in MOI.
1515
"""
16-
mutable struct GeometricConicForm{T,AT,VB,VC,C} <: MOI.ModelLike
17-
num_rows::Vector{Int}
18-
dimension::Dict{Int,Int}
19-
sense::MOI.OptimizationSense
20-
objective_constant::T # The objective
21-
A::Union{Nothing,AT} # The constraints
22-
b::VB # `b - Ax in cones`
23-
c::VC # `sense c'x + objective_constant`
24-
cone_types::C
25-
cone_types_dict::Dict{DataType,Int}
26-
27-
function GeometricConicForm{T,AT,VB,VC}(cone_types) where {T,AT,VB,VC}
28-
model = new{T,AT,VB,VC,typeof(cone_types)}()
29-
model.cone_types = cone_types
30-
model.cone_types_dict =
31-
Dict{DataType,Int}(s => i for (i, s) in enumerate(cone_types))
32-
model.num_rows = zeros(Int, length(cone_types))
33-
model.dimension = Dict{Int,Int}()
34-
model.A = nothing
35-
return model
36-
end
37-
end
38-
39-
function GeometricConicForm{T,AT,VT}(cone_types) where {T,AT,VT}
40-
return GeometricConicForm{T,AT,VT,VT}(cone_types)
41-
end
42-
43-
_set_type(::MOI.ConstraintIndex{F,S}) where {F,S} = S
44-
45-
MOI.is_empty(model::GeometricConicForm) = model.A === nothing
46-
47-
function MOI.empty!(model::GeometricConicForm{T}) where {T}
48-
empty!(model.dimension)
49-
fill!(model.num_rows, 0)
50-
model.A = nothing
51-
model.sense = MOI.FEASIBILITY_SENSE
52-
return model.objective_constant = zero(T)
53-
end
54-
55-
function MOI.supports_constraint(
56-
model::GeometricConicForm{T},
57-
::Type{MOI.VectorAffineFunction{T}},
58-
::Type{S},
59-
) where {T,S<:MOI.AbstractVectorSet}
60-
return haskey(model.cone_types_dict, S)
61-
end
62-
63-
function _allocate_variables(
64-
model::GeometricConicForm{T,AT,VT},
65-
vis_src,
66-
idxmap,
67-
) where {T,AT,VT}
68-
model.A = AT(length(vis_src))
69-
for (i, vi) in enumerate(vis_src)
70-
idxmap[vi] = MOI.VariableIndex(i)
71-
end
72-
return
73-
end
74-
75-
function rows(
76-
model::GeometricConicForm{T},
77-
ci::CI{MOI.VectorAffineFunction{T}},
78-
) where {T}
79-
return ci.value .+ (1:model.dimension[ci.value])
80-
end
81-
82-
function MOI.set(
83-
::GeometricConicForm,
84-
::MOI.VariablePrimalStart,
85-
::MOI.VariableIndex,
86-
::Nothing,
87-
)
88-
return
89-
end
90-
91-
function MOI.set(
92-
model::GeometricConicForm{T},
93-
::MOI.VariablePrimalStart,
94-
vi::MOI.VariableIndex,
95-
value::T,
96-
) where {T}
97-
return model.primal[vi.value] = value
98-
end
99-
100-
function MOI.set(
101-
::GeometricConicForm,
102-
::MOI.ConstraintPrimalStart,
103-
::MOI.ConstraintIndex,
104-
::Nothing,
105-
)
106-
return
107-
end
108-
109-
function MOI.set(
110-
model::GeometricConicForm,
111-
::MOI.ConstraintPrimalStart,
112-
ci::MOI.ConstraintIndex,
113-
value,
114-
)
115-
offset = constroffset(model, ci)
116-
return model.slack[rows(model, ci)] .= value
117-
end
118-
119-
function MOI.set(
120-
::GeometricConicForm,
121-
::MOI.ConstraintDualStart,
122-
::MOI.ConstraintIndex,
123-
::Nothing,
124-
)
125-
return
126-
end
127-
128-
function MOI.set(
129-
model::GeometricConicForm,
130-
::MOI.ConstraintDualStart,
131-
ci::MOI.ConstraintIndex,
132-
value,
133-
)
134-
offset = constroffset(model, ci)
135-
return model.dual[rows(model, ci)] .= value
136-
end
137-
138-
function MOI.set(
139-
model::GeometricConicForm,
140-
::MOI.ObjectiveSense,
141-
sense::MOI.OptimizationSense,
142-
)
143-
return model.sense = sense
144-
end
145-
146-
variable_index_value(t::MOI.ScalarAffineTerm) = t.variable.value
147-
148-
function variable_index_value(t::MOI.VectorAffineTerm)
149-
return variable_index_value(t.scalar_term)
150-
end
151-
152-
function MOI.set(
153-
model::GeometricConicForm{T},
154-
::MOI.ObjectiveFunction,
155-
f::MOI.ScalarAffineFunction{T},
156-
) where {T}
157-
c = Vector(
158-
sparsevec(
159-
variable_index_value.(f.terms),
160-
MOI.coefficient.(f.terms),
161-
model.A.n,
162-
),
16+
function empty_geometric_conic_form(cones; Tv = Float64, Ti = Int, I = MOI.Utilities.OneBasedIndexing)
17+
return MOI.Utilities.GenericModel{T}(
18+
MOI.Utilities.ObjectiveContainer{T}(),
19+
MOI.Utilities.FreeVariables(),
20+
MOI.Utilities.MatrixOfConstraints{
21+
T,
22+
MOI.Utilities.MutableSparseMatrixCSC{
23+
Tv,
24+
Ti,
25+
I,
26+
},
27+
Vector{T},
28+
ProductOfSets{T},
29+
},
16330
)
164-
model.objective_constant = f.constant
165-
model.c = c
166-
return
167-
end
168-
169-
function _allocate_constraint(
170-
model::GeometricConicForm,
171-
src,
172-
indexmap,
173-
cone_id,
174-
ci,
175-
)
176-
# TODO use `CanonicalConstraintFunction`
177-
func = MOI.get(src, MOI.ConstraintFunction(), ci)
178-
func = MOIU.is_canonical(func) ? func : MOI.Utilities.canonical(func)
179-
allocate_terms(model.A, indexmap, func)
180-
offset = model.num_rows[cone_id]
181-
model.num_rows[cone_id] = offset + MOI.output_dimension(func)
182-
return ci, offset, func
18331
end
18432

185-
function _allocate_constraints(
186-
model::GeometricConicForm{T},
187-
src,
188-
indexmap,
189-
cone_id,
190-
::Type{S},
191-
) where {T,S}
192-
cis = MOI.get(
193-
src,
194-
MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T},S}(),
195-
)
196-
return map(cis) do ci
197-
return _allocate_constraint(model, src, indexmap, cone_id, ci)
198-
end
33+
function geometric_conic_form(model::MOI.ModelLike, cones; kws...)
34+
form = empty_geometric_conic_form(cones; kws...)
35+
index_map = MOI.copy_to(form, model)
36+
return form, index_map
19937
end
20038

201-
function _load_variables(model::GeometricConicForm, nvars::Integer)
202-
m = sum(model.num_rows)
203-
model.A.m = m
204-
model.b = zeros(m)
205-
model.c = zeros(model.A.n)
206-
return allocate_nonzeros(model.A)
207-
end
39+
_coef_type(::MOI.Utilities.AbstractModel{T}) where {T} = T
20840

209-
function _load_constraints(
210-
model::GeometricConicForm,
211-
src,
212-
indexmap,
213-
cone_offset,
214-
i,
215-
cache,
216-
)
217-
for (ci_src, offset_in_cone, func) in cache
218-
offset = cone_offset + offset_in_cone
219-
set = MOI.get(src, MOI.ConstraintSet(), ci_src)
220-
load_terms(model.A, indexmap, func, offset)
221-
copyto!(model.b, offset + 1, func.constants)
222-
model.dimension[offset] = MOI.output_dimension(func)
223-
indexmap[ci_src] = typeof(ci_src)(offset)
224-
end
225-
end
226-
227-
function MOI.copy_to(dest::GeometricConicForm{T}, src::MOI.ModelLike) where {T}
228-
MOI.empty!(dest)
229-
vis_src = MOI.get(src, MOI.ListOfVariableIndices())
230-
idxmap = MOIU.IndexMap()
231-
has_constraints = BitSet()
232-
for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
233-
i = get(dest.cone_types_dict, S, nothing)
234-
if i === nothing || F != MOI.VectorAffineFunction{T}
235-
throw(MOI.UnsupportedConstraint{F,S}())
236-
end
237-
push!(has_constraints, i)
238-
end
239-
_allocate_variables(dest, vis_src, idxmap)
240-
# Allocate constraints
241-
caches = map(collect(has_constraints)) do i
242-
return _allocate_constraints(dest, src, idxmap, i, dest.cone_types[i])
243-
end
244-
# Load variables
245-
_load_variables(dest, length(vis_src))
246-
# Set variable attributes
247-
MOIU.pass_attributes(dest, src, idxmap, vis_src)
248-
# Set model attributes
249-
MOIU.pass_attributes(dest, src, idxmap)
250-
# Load constraints
251-
offset = 0
252-
for (i, cache) in zip(has_constraints, caches)
253-
_load_constraints(dest, src, idxmap, offset, i, cache)
254-
offset += dest.num_rows[i]
41+
function objective_vector(model::MOI.ModelLike; T = _coef_type(model))
42+
obj = MOI.get(src, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}())
43+
dest.objective_constant = MOI.constant(obj)
44+
c = zeros(A.n)
45+
for term in obj.terms
46+
c[term.variable.value] += term.coefficient
25547
end
256-
final_touch(dest.A)
257-
return idxmap
48+
return c
25849
end

src/product_of_sets.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright (c) 2019: Joaquim Dias Garcia, and contributors
2+
#
3+
# Use of this source code is governed by an MIT-style license that can be found
4+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
5+
# Copied from DiffOpt
6+
7+
"""
8+
ProductOfSets{T} <: MOI.Utilities.OrderedProductOfSets{T}
9+
10+
The `MOI.Utilities.@product_of_sets` macro requires to know the list of sets
11+
at compile time. In DiffOpt however, the list depends on what the user is going
12+
to use as set as DiffOpt supports any set as long as it implements the
13+
required function of MathOptSetDistances.
14+
For this type, the list of sets can be given a run-time.
15+
"""
16+
mutable struct ProductOfSets{T} <: MOI.Utilities.OrderedProductOfSets{T}
17+
"""
18+
During the copy, this counts the number of rows corresponding to
19+
each set. At the end of copy, `final_touch` is called, which
20+
converts this list into a cumulative ordering.
21+
"""
22+
num_rows::Vector{Int}
23+
24+
"""
25+
A dictionary which maps the `set_index` and `offset` of a set to the
26+
dimension, i.e., `dimension[(set_index,offset)] → dim`.
27+
"""
28+
dimension::Dict{Tuple{Int,Int},Int}
29+
30+
"""
31+
A sanity bit to check that we don't call functions out-of-order.
32+
"""
33+
final_touch::Bool
34+
35+
set_types::Vector{Type}
36+
set_types_dict::Dict{Type,Int}
37+
38+
function ProductOfSets{T}() where {T}
39+
return new(
40+
Int[],
41+
Dict{Tuple{Int,Int},Int}(),
42+
false,
43+
Type[],
44+
Dict{Type,Int}(),
45+
)
46+
end
47+
end
48+
49+
function MOI.Utilities.set_index(set::ProductOfSets, S::Type{<:MOI.AbstractSet})
50+
return get(set.set_types_dict, S, nothing)
51+
end
52+
53+
MOI.Utilities.set_types(set::ProductOfSets) = set.set_types
54+
55+
function set_set_types(set::ProductOfSets, set_types)
56+
resize!(set.num_rows, length(set_types))
57+
fill!(set.num_rows, 0)
58+
resize!(set.set_types, length(set_types))
59+
copy!(set.set_types, set_types)
60+
empty!(set.set_types_dict)
61+
for i in eachindex(set_types)
62+
set.set_types_dict[set_types[i]] = i
63+
end
64+
return
65+
end
66+
67+
function add_set_types(set::ProductOfSets, S::Type)
68+
if !haskey(set.set_types_dict, S)
69+
push!(set.num_rows, 0)
70+
push!(set.set_types, S)
71+
set.set_types_dict[S] = length(set.set_types)
72+
return true
73+
end
74+
return false
75+
end

0 commit comments

Comments
 (0)