|
4 | 4 | # in the LICENSE.md file or at https://opensource.org/licenses/MIT. |
5 | 5 |
|
6 | 6 | """ |
7 | | - GeometricConicForm{T, AT, VT, C} <: MOI.ModelLike |
| 7 | + empty_geometric_conic_form |
8 | 8 |
|
9 | 9 | Represents an optimization model of the form: |
10 | 10 | ``` |
11 | 11 | sense ⟨c, x⟩ + c0 |
12 | | -s.t. b_i - A_i x ∈ C_i ∀ i |
| 12 | +s.t. A_i x + b_i ∈ C_i ∀ i |
13 | 13 | ``` |
14 | 14 | with each `C_i` a cone defined in MOI. |
15 | 15 | """ |
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( |
| 17 | + cones; |
| 18 | + Tv = Float64, |
| 19 | + Ti = Int, |
| 20 | + I = MOI.Utilities.OneBasedIndexing, |
| 21 | +) |
| 22 | + model = MOI.Utilities.GenericModel{Tv}( |
| 23 | + MOI.Utilities.ObjectiveContainer{Tv}(), |
| 24 | + MOI.Utilities.FreeVariables(), |
| 25 | + MOI.Utilities.MatrixOfConstraints{ |
| 26 | + Tv, |
| 27 | + MOI.Utilities.MutableSparseMatrixCSC{Tv,Ti,I}, |
| 28 | + Vector{Tv}, |
| 29 | + ProductOfSets{Tv}, |
| 30 | + }(), |
163 | 31 | ) |
164 | | - model.objective_constant = f.constant |
165 | | - model.c = c |
166 | | - return |
| 32 | + set_set_types(model.constraints.sets, cones) |
| 33 | + return model |
167 | 34 | end |
168 | 35 |
|
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 |
| 36 | +function geometric_conic_form(model::MOI.ModelLike, cones; kws...) |
| 37 | + form = empty_geometric_conic_form(cones; kws...) |
| 38 | + index_map = MOI.copy_to(form, model) |
| 39 | + return form, index_map |
183 | 40 | end |
184 | 41 |
|
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 |
199 | | -end |
200 | | - |
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 |
| 42 | +_coef_type(::MOI.Utilities.AbstractModel{T}) where {T} = T |
208 | 43 |
|
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] |
| 44 | +function objective_vector(model::MOI.ModelLike; T = _coef_type(model)) |
| 45 | + obj = MOI.get(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()) |
| 46 | + c = zeros(MOI.get(model, MOI.NumberOfVariables())) |
| 47 | + for term in obj.terms |
| 48 | + c[term.variable.value] += term.coefficient |
255 | 49 | end |
256 | | - final_touch(dest.A) |
257 | | - return idxmap |
| 50 | + return c |
258 | 51 | end |
0 commit comments