Skip to content

Commit 19ab3a9

Browse files
authored
[breaking] Cleanup the Context object: remove id_hash, simplify dictionaries (#645)
* `detected_infeasible_during_formulation` does not need to be a ref * rm `id_hash`es, use `IdDict` directly * format
1 parent 16c4fe5 commit 19ab3a9

File tree

12 files changed

+61
-110
lines changed

12 files changed

+61
-110
lines changed

docs/src/examples/mixed_integer/aux_files/antidiag.jl

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ export antidiag
1414

1515
### Diagonal
1616
### Represents the kth diagonal of an mxn matrix as a (min(m, n) - k) x 1 vector
17-
struct AntidiagAtom <: Convex.AbstractExpr
17+
mutable struct AntidiagAtom <: Convex.AbstractExpr
1818
head::Symbol
19-
id_hash::UInt64
2019
children::Tuple{Convex.AbstractExpr}
2120
size::Tuple{Int,Int}
2221
k::Int
@@ -27,13 +26,7 @@ struct AntidiagAtom <: Convex.AbstractExpr
2726
error("Bounds error in calling diag")
2827
end
2928
children = (x,)
30-
return new(
31-
:antidiag,
32-
hash((children, k)),
33-
children,
34-
(minimum(x.size) - k, 1),
35-
k,
36-
)
29+
return new(:antidiag, children, (minimum(x.size) - k, 1), k)
3730
end
3831
end
3932

docs/src/manual/advanced.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,15 @@ this. To do so, we define
131131

132132
```@example 1
133133
using Convex
134+
135+
# Must be mutable! Otherwise variables with the same size/value would be treated as the same object.
134136
mutable struct ProbabilityVector <: Convex.AbstractVariable
135137
head::Symbol
136-
id_hash::UInt64
137-
size::Tuple{Int, Int}
138+
size::Tuple{Int,Int}
138139
value::Union{Convex.Value,Nothing}
139140
vexity::Convex.Vexity
140141
function ProbabilityVector(d)
141-
this = new(:ProbabilityVector, 0, (d,1), nothing, Convex.AffineVexity())
142-
this.id_hash = objectid(this)
143-
this
142+
return new(:ProbabilityVector, (d, 1), nothing, Convex.AffineVexity())
144143
end
145144
end
146145
@@ -165,8 +164,8 @@ solve!(prob, SCS.Optimizer)
165164
evaluate(p) # [1.0, 0.0, 0.0]
166165
```
167166

168-
Subtypes of `AbstractVariable` must have the fields `head`, `id_hash`, and
169-
`size`, and `id_hash` must be populated as shown in the example. Then they must also
167+
Subtypes of `AbstractVariable` must have the fields `head` and
168+
`size`. Then they must also
170169

171170
* either have a field `value`, or implement [`Convex._value`](@ref) and
172171
[`Convex.set_value!`](@ref)

src/Context.jl

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,20 @@ mutable struct Context{T,M}
88
model::M
99

1010
# Used for populating variable values after solving
11-
var_id_to_moi_indices::OrderedCollections.OrderedDict{
12-
UInt64,
11+
var_to_moi_indices::IdDict{
12+
Any,
1313
Union{
1414
Vector{MOI.VariableIndex},
1515
Tuple{Vector{MOI.VariableIndex},Vector{MOI.VariableIndex}},
1616
},
1717
}
18-
# `id_hash` -> `AbstractVariable`
19-
id_to_variables::OrderedCollections.OrderedDict{UInt64,Any}
2018

2119
# Used for populating constraint duals
2220
constr_to_moi_inds::IdDict{Any,Any}
2321

24-
detected_infeasible_during_formulation::Ref{Bool}
22+
detected_infeasible_during_formulation::Bool
2523

2624
# Cache
27-
# conic_form_cache::DataStructures.WeakKeyIdDict{Any, Any}
2825
conic_form_cache::IdDict{Any,Any}
2926
end
3027

@@ -39,8 +36,13 @@ function Context{T}(optimizer_factory; add_cache::Bool = false) where {T}
3936
end
4037
return Context{T,typeof(model)}(
4138
model,
42-
OrderedCollections.OrderedDict{UInt64,Vector{MOI.VariableIndex}}(),
43-
OrderedCollections.OrderedDict{UInt64,Any}(),
39+
IdDict{
40+
Any,
41+
Union{
42+
Vector{MOI.VariableIndex},
43+
Tuple{Vector{MOI.VariableIndex},Vector{MOI.VariableIndex}},
44+
},
45+
}(),
4446
IdDict{Any,Any}(),
4547
false,
4648
IdDict{Any,Any}(),
@@ -49,10 +51,9 @@ end
4951

5052
function Base.empty!(context::Context)
5153
MOI.empty!(context.model)
52-
empty!(context.var_id_to_moi_indices)
53-
empty!(context.id_to_variables)
54+
empty!(context.var_to_moi_indices)
5455
empty!(context.constr_to_moi_inds)
55-
context.detected_infeasible_during_formulation[] = false
56+
context.detected_infeasible_during_formulation = false
5657
empty!(context.conic_form_cache)
5758
return
5859
end

src/MOI_wrapper.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
struct Optimizer{T,M} <: MOI.AbstractOptimizer
77
context::Context{T,M}
8-
moi_to_convex::OrderedCollections.OrderedDict{MOI.VariableIndex,UInt64}
8+
moi_to_convex::OrderedCollections.OrderedDict{MOI.VariableIndex,Any}
99
convex_to_moi::Dict{UInt64,Vector{MOI.VariableIndex}}
1010
constraint_map::Vector{MOI.ConstraintIndex}
1111
function Optimizer(context::Context{T,M}) where {T,M}
@@ -47,9 +47,8 @@ end
4747

4848
function _add_variable(model::Optimizer, vi::MOI.VariableIndex)
4949
var = Variable()
50-
model.moi_to_convex[vi] = var.id_hash
51-
model.context.var_id_to_moi_indices[var.id_hash] = [vi]
52-
model.context.id_to_variables[var.id_hash] = var
50+
model.moi_to_convex[vi] = var
51+
model.context.var_to_moi_indices[var] = [vi]
5352
return
5453
end
5554

@@ -129,7 +128,7 @@ function _expr(::Optimizer, v::Value)
129128
end
130129

131130
function _expr(model::Optimizer, x::MOI.VariableIndex)
132-
return model.context.id_to_variables[model.moi_to_convex[x]]
131+
return model.moi_to_convex[x]
133132
end
134133

135134
function _expr(model::Optimizer, f::MOI.AbstractScalarFunction)

src/constant.jl

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ end
3838

3939
mutable struct Constant{T<:Real} <: AbstractExpr
4040
head::Symbol
41-
id_hash::UInt64
4241
value::Union{Matrix{T},SPARSE_MATRIX{T}}
4342
size::Tuple{Int,Int}
4443
sign::Sign
@@ -47,13 +46,7 @@ mutable struct Constant{T<:Real} <: AbstractExpr
4746
if x isa Complex || x isa AbstractArray{<:Complex}
4847
throw(DomainError(x, "Constant expects real values"))
4948
end
50-
return new{eltype(x)}(
51-
:constant,
52-
objectid(x),
53-
_matrix(x),
54-
_size(x),
55-
sign,
56-
)
49+
return new{eltype(x)}(:constant, _matrix(x), _size(x), sign)
5750
end
5851
end
5952
function Constant(x::Value, check_sign::Bool = true)
@@ -63,13 +56,12 @@ end
6356

6457
mutable struct ComplexConstant{T<:Real} <: AbstractExpr
6558
head::Symbol
66-
id_hash::UInt64
6759
size::Tuple{Int,Int}
6860
real_constant::Constant{T}
6961
imag_constant::Constant{T}
7062
function ComplexConstant(re::Constant{T}, im::Constant{T}) where {T}
7163
size(re) == size(im) || error("size mismatch")
72-
return new{T}(:complex_constant, rand(UInt64), size(re), re, im)
64+
return new{T}(:complex_constant, size(re), re, im)
7365
end
7466

7567
# function ComplexConstant(re::Constant{S1}, im::Constant{S2}) where {S1,S2}

src/constraints/GenericConstraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ end
8787
function _add_constraint!(context::Context, c::GenericConstraint)
8888
if vexity(c.child) == ConstVexity()
8989
if !is_feasible(evaluate(c.child), c.set, CONSTANT_CONSTRAINT_TOL[])
90-
context.detected_infeasible_during_formulation[] = true
90+
context.detected_infeasible_during_formulation = true
9191
end
9292
return
9393
end

src/expressions.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ const Value = Union{Number,AbstractArray}
4040
# We commandeer `==` to create a constraint.
4141
# Therefore we define `isequal` to still have a notion of equality
4242
# (Normally `isequal` falls back to `==`, so we need to provide a method).
43-
# All `AbstractExpr` (Constraints are not AbstractExpr's!) are compared by value, except for AbstractVariables,
44-
# which use their `id_hash` field.
43+
# All `AbstractExpr` (Constraints are not AbstractExpr's!) are compared by value, except for AbstractVariables, which are compared by `===` (objectid).
4544
function Base.isequal(x::AbstractExpr, y::AbstractExpr)
4645
if typeof(x) != typeof(y)
4746
return false

src/solution.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ end
3939

4040
function _add_variable_primal_start(context::Convex.Context{T}) where {T}
4141
attr = MOI.VariablePrimalStart()
42-
for (id, moi_indices) in context.var_id_to_moi_indices
43-
x = context.id_to_variables[id]
42+
for (x, moi_indices) in context.var_to_moi_indices
4443
if x.value === nothing
4544
continue
4645
elseif moi_indices isa Tuple # Variable is complex
@@ -97,7 +96,7 @@ function solve!(
9796
if warmstart && MOI.supports(context.model, attr, MOI.VariableIndex)
9897
_add_variable_primal_start(context)
9998
end
100-
if context.detected_infeasible_during_formulation[]
99+
if context.detected_infeasible_during_formulation
101100
p.status = MOI.INFEASIBLE
102101
else
103102
MOI.optimize!(context.model)
@@ -108,8 +107,7 @@ function solve!(
108107
@warn "Problem wasn't solved optimally" status = p.status
109108
end
110109
primal_status = MOI.get(context.model, MOI.PrimalStatus())
111-
for (id, indices) in context.var_id_to_moi_indices
112-
var = context.id_to_variables[id]
110+
for (var, indices) in context.var_to_moi_indices
113111
if vexity(var) == ConstVexity()
114112
continue # Fixed variable
115113
elseif primal_status == MOI.NO_SOLUTION

src/utilities/show.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using .TreePrint
88
"""
99
show_id(io::IO, x::Union{AbstractVariable}; digits = 3)
1010
11-
Print a truncated version of the objects `id_hash` field.
11+
Print a truncated version of the object's id.
1212
1313
## Example
1414
@@ -19,12 +19,12 @@ julia> Convex.show_id(stdout, x)
1919
id: 163…906
2020
```
2121
"""
22-
function show_id(io::IO, x::Union{AbstractVariable}; digits = MAXDIGITS[])
22+
function show_id(io::IO, x::AbstractVariable; digits = MAXDIGITS[])
2323
return print(io, show_id(x; digits = digits))
2424
end
2525

26-
function show_id(x::Union{AbstractVariable}; digits = MAXDIGITS[])
27-
hash_str = string(x.id_hash)
26+
function show_id(x::AbstractVariable; digits = MAXDIGITS[])
27+
hash_str = string(objectid(x))
2828
if length(hash_str) > (2 * digits + 1)
2929
return "id: " * first(hash_str, digits) * "" * last(hash_str, digits)
3030
else

src/variable.jl

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ integer-valued (`IntVar`), or binary (`BinVar`).
1818
"""
1919
abstract type AbstractVariable <: AbstractExpr end
2020
21-
An `AbstractVariable` should have `head` field, an `id_hash` field
22-
and a `size` field to conform to the `AbstractExpr` interface, and
21+
An `AbstractVariable` should have `head` field, and a `size` field to conform to the `AbstractExpr` interface, and
2322
implement methods (or use the field-access fallbacks) for
2423
2524
* [`_value`](@ref), [`set_value!`](@ref): get or set the numeric value of the variable.
@@ -188,10 +187,10 @@ function free!(x::AbstractVariable)
188187
end
189188

190189
function Base.isequal(x::AbstractVariable, y::AbstractVariable)
191-
return x.id_hash == y.id_hash
190+
return x === y
192191
end
193192

194-
Base.hash(x::AbstractVariable, h::UInt) = hash(x.id_hash, h)
193+
Base.hash(x::AbstractVariable, h::UInt) = hash(objectid(x), h)
195194

196195
iscomplex(x::Sign) = x == ComplexSign()
197196

@@ -204,8 +203,6 @@ iscomplex(::Union{Real,AbstractArray{<:Real}}) = false
204203
mutable struct Variable <: AbstractVariable
205204
# Every `AbstractExpr` has a `head`; for a Variable it is set to `:variable`.
206205
head::Symbol
207-
# A unique identifying hash used for caching.
208-
id_hash::UInt64
209206
# The current value of the variable. Defaults to `nothing` until the
210207
# variable has been [`fix!`](@ref)'d to a particular value, or the
211208
# variable has been used in a problem which has been solved, at which
@@ -243,18 +240,15 @@ mutable struct Variable <: AbstractVariable
243240
),
244241
)
245242
end
246-
this = new(
243+
return new(
247244
:variable,
248-
0,
249245
nothing,
250246
size,
251247
AffineVexity(),
252248
sign,
253249
Constraint[],
254250
vartype,
255251
)
256-
this.id_hash = objectid(this)
257-
return this
258252
end
259253
end
260254

@@ -274,7 +268,6 @@ Variable(vartype::VarType) = Variable((1, 1), NoSign(), vartype)
274268

275269
mutable struct ComplexVariable <: AbstractVariable
276270
head::Symbol
277-
id_hash::UInt64
278271
size::Tuple{Int,Int}
279272
value::Union{Value,Nothing}
280273
vexity::Vexity
@@ -283,7 +276,6 @@ mutable struct ComplexVariable <: AbstractVariable
283276
function ComplexVariable(size::Tuple{Int,Int} = (1, 1))
284277
return new(
285278
:ComplexVariable,
286-
rand(UInt64),
287279
size,
288280
nothing,
289281
AffineVexity(),

0 commit comments

Comments
 (0)