Skip to content

Commit 599d5bd

Browse files
authored
Merge pull request #63 from JuliaOpt/ml/memory
address memory corruption issue
2 parents a8e9679 + 7a4926b commit 599d5bd

File tree

4 files changed

+48
-73
lines changed

4 files changed

+48
-73
lines changed

src/ECOS.jl

Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,23 @@ function __init__()
4343
end
4444
end
4545

46-
include("ECOSSolverInterface.jl") # MathProgBase interface
4746
include("types.jl") # All the types and constants defined in ecos.h
4847

48+
# A julia container for matrices passed to ECOS.
49+
# This makes it easy for the Julia GC to track these arrays.
50+
# The format matches ECOS's spmat: 0-based compressed sparse column (CSC)
51+
struct ECOSMatrix
52+
pr::Vector{Cdouble} # nzval
53+
jc::Vector{Clong} # colptr
54+
ir::Vector{Clong} # rowval
55+
end
56+
57+
function ECOSMatrix(mat::AbstractMatrix)
58+
sparsemat = sparse(mat)
59+
return ECOSMatrix(sparsemat.nzval, sparsemat.colptr .- 1, sparsemat.rowval .- 1)
60+
end
61+
62+
4963
# setup (direct interface)
5064
# Provide ECOS with a problem in the form
5165
# min c'x
@@ -66,35 +80,33 @@ include("types.jl") # All the types and constants defined in ecos.h
6680
# e.g. cone 1 is indices 4:6, cone 2 is indices 7:10
6781
# -> q = [3, 4]
6882
# e Number of exponential cones present in problem
69-
# Gpr, Gjc, Gir
70-
# Non-zeros, column indices, and the row index arrays, respectively,
71-
# for the matrix G represented in column compressed storage (CCS) format
72-
# which is equivalent to Julia's SparseMatrixCSC
73-
# Apr, Ajc, Air
74-
# Equivalent to above for the matrix A.
83+
# G The G matrix in ECOSMatrix format.
84+
# A The A matrix in ECOSMatrix format.
7585
# Can be all nothing if no equalities are present.
7686
# c Objective coefficients, length(c) == n
7787
# h RHS for inequality constraints, length(h) == m
7888
# b RHS for equality constraints, length(b) == b (can be nothing)
7989
# Returns a pointer to the ECOS pwork structure (Cpwork in ECOS.jl). See
8090
# types.jl for more information.
91+
# **NOTE**: ECOS retains references to the problem data passed in here.
92+
# You *must* ensure that G, A, c, h, and b are not freed until after cleanup(), otherwise
93+
# memory corruption will occur.
8194
function setup(n::Int, m::Int, p::Int, l::Int, ncones::Int, q::Union{Vector{Int},Void}, e::Int,
82-
Gpr::Vector{Float64}, Gjc::Vector{Int}, Gir::Vector{Int},
83-
Apr::Union{Vector{Float64},Void}, Ajc::Union{Vector{Int},Void}, Air::Union{Vector{Int},Void},
84-
c::Vector{Float64}, h::Vector{Float64}, b::Union{Vector{Float64},Void}; kwargs...)
95+
G::ECOSMatrix, A::Union{ECOSMatrix, Void},
96+
c::Vector{Float64}, h::Vector{Float64}, b::Union{Vector{Float64},Void}; kwargs...)
8597
# Convert to canonical forms
8698
q = (q == nothing) ? convert(Ptr{Clong}, C_NULL) : convert(Vector{Clong},q)
87-
Apr = (Apr == nothing) ? convert(Ptr{Cdouble}, C_NULL) : Apr
88-
Ajc = (Ajc == nothing) ? convert(Ptr{Cdouble}, C_NULL) : convert(Vector{Clong},Ajc)
89-
Air = (Air == nothing) ? convert(Ptr{Cdouble}, C_NULL) : convert(Vector{Clong},Air)
99+
Apr = (A == nothing) ? convert(Ptr{Cdouble}, C_NULL) : A.pr
100+
Ajc = (A == nothing) ? convert(Ptr{Cdouble}, C_NULL) : A.jc
101+
Air = (A == nothing) ? convert(Ptr{Cdouble}, C_NULL) : A.ir
90102
b = (b == nothing) ? convert(Ptr{Cdouble}, C_NULL) : b
91103
problem_ptr = ccall((:ECOS_setup, ECOS.ecos), Ptr{Cpwork},
92104
(Clong, Clong, Clong, Clong, Clong, Ptr{Clong}, Clong,
93105
Ptr{Cdouble}, Ptr{Clong}, Ptr{Clong},
94106
Ptr{Cdouble}, Ptr{Clong}, Ptr{Clong},
95107
Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}),
96108
n, m, p, l, ncones, q, e,
97-
Gpr, convert(Vector{Clong},Gjc), convert(Vector{Clong},Gir),
109+
G.pr, G.jc, G.ir,
98110
Apr, Ajc, Air,
99111
c, h, b)
100112

@@ -122,47 +134,6 @@ function setup(n::Int, m::Int, p::Int, l::Int, ncones::Int, q::Union{Vector{Int}
122134
problem_ptr
123135
end
124136

125-
# setup (more general interface)
126-
# A more tolerant version of the above method that doesn't require
127-
# user to fiddle with internals of the sparse matrix format
128-
# User can pass nothing as argument for A, b, and q
129-
function setup(n, m, p, l, ncones, q, e, G, A, c, h, b; options...)
130-
@assert m == l + sum(q) + 3e
131-
@assert length(c) == n
132-
@assert ncones == length(q)
133-
if A == nothing
134-
if b != nothing
135-
@assert length(b) == 0
136-
b = nothing
137-
end
138-
@assert p == 0
139-
Apr = nothing
140-
Ajc = nothing
141-
Air = nothing
142-
else
143-
numrow, numcol = size(A)
144-
@assert numcol == n
145-
@assert numrow == length(b)
146-
@assert numrow == p
147-
sparseA = sparse(A)
148-
Apr = convert(Vector{Float64}, sparseA.nzval)
149-
Ajc = sparseA.colptr - 1 # C is 0-based
150-
Air = sparseA.rowval - 1
151-
end
152-
153-
@assert size(G) == (m, n)
154-
@assert m == length(h)
155-
sparseG = sparse(G)
156-
Gpr = convert(Vector{Float64}, sparseG.nzval)
157-
Gjc = sparseG.colptr - 1 # C is 0-based
158-
Gir = sparseG.rowval - 1
159-
160-
setup( n, m, p, l, ncones, q, e,
161-
Gpr, Gjc, Gir,
162-
Apr, Ajc, Air,
163-
c, h, b; options...)
164-
end
165-
166137
# solve
167138
# Solves the provided problem. Results are stored inside the structure,
168139
# but currently there is no convenient interface-provided way to access
@@ -178,4 +149,6 @@ function cleanup(problem::Ptr{Cpwork}, keepvars::Int = 0)
178149
ccall((:ECOS_cleanup, ECOS.ecos), Void, (Ptr{Cpwork}, Clong), problem, keepvars)
179150
end
180151

152+
include("ECOSSolverInterface.jl") # MathProgBase interface
153+
181154
end # module

src/ECOSSolverInterface.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ mutable struct ECOSMathProgModel <: AbstractConicModel
2626
ncones::Int # Number of SO cones
2727
conedims::Vector{Int} # Dimension of each SO cone
2828
nexp_cones::Int # Number of exponential cones
29-
G::SparseMatrixCSC{Float64,Int} # The G matrix (inequalties)
30-
A::SparseMatrixCSC{Float64,Int} # The A matrix (equalities)
29+
G::ECOSMatrix # The G matrix (inequalties)
30+
A::ECOSMatrix # The A matrix (equalities)
3131
c::Vector{Float64} # The objective coeffs (always min)
3232
orig_sense::Symbol # Original objective sense
3333
h::Vector{Float64} # RHS for inequality
@@ -55,8 +55,8 @@ mutable struct ECOSMathProgModel <: AbstractConicModel
5555
end
5656
ECOSMathProgModel(;kwargs...) = ECOSMathProgModel(0,0,0,0,0,0,
5757
Int[],0,
58-
spzeros(0,0),
59-
spzeros(0,0),
58+
ECOSMatrix(spzeros(0,0)),
59+
ECOSMatrix(spzeros(0,0)),
6060
Float64[], :Min,
6161
Float64[], Float64[],
6262
:NotSolved, 0.0, 0.0,
@@ -85,8 +85,9 @@ function optimize!(m::ECOSMathProgModel)
8585
m.nvar, m.nineq, m.neq,
8686
m.npos, m.ncones, m.conedims, m.nexp_cones,
8787
m.G, m.A,
88-
m.c[:], # Seems to modify this
88+
m.c,
8989
m.h, m.b; m.options...)
90+
# Note: ECOS modifies problem data in setup() and restores it on cleanup()
9091

9192
t = time()
9293
flag = solve(ecos_prob_ptr)
@@ -109,8 +110,9 @@ function optimize!(m::ECOSMathProgModel)
109110
m.primal_sol = unsafe_wrap(Array,ecos_prob.x, m.nvar)[:]
110111
m.dual_sol_eq = unsafe_wrap(Array,ecos_prob.y, m.neq)[:]
111112
m.dual_sol_ineq = unsafe_wrap(Array,ecos_prob.z, m.nineq)[:]
112-
m.obj_val = dot(m.c, m.primal_sol) * (m.orig_sense == :Max ? -1 : +1)
113113
cleanup(ecos_prob_ptr, 0)
114+
# Compute this value after cleanup with restored c.
115+
m.obj_val = dot(m.c, m.primal_sol) * (m.orig_sense == :Max ? -1 : +1)
114116
end
115117

116118
status(m::ECOSMathProgModel) = m.solve_stat
@@ -391,8 +393,8 @@ function loadproblem!(m::ECOSMathProgModel, c, A, b, constr_cones, var_cones)
391393
m.ncones = num_SOC_cones # Num second-order cones
392394
m.conedims = SOC_conedims # Num contr. in each SOC
393395
m.nexp_cones = num_exp_cones # Num exponential cones
394-
m.G = ecos_G
395-
m.A = ecos_A
396+
m.G = ECOSMatrix(ecos_G)
397+
m.A = ECOSMatrix(ecos_A)
396398
m.c = ecos_c
397399
m.orig_sense = :Min
398400
m.h = ecos_h

0 commit comments

Comments
 (0)