From c5f432d4b0184116cc0f60ca58a97b87eb8e3a0e Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 17:10:20 -0500 Subject: [PATCH 1/8] remove duplicate method definitions --- src/PATHSolver.jl | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index 40db786..0a46314 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -15,37 +15,12 @@ export solveMCP, options - -function solveMCP(f_eval::Function, lb::Vector, ub::Vector) - var_name = C_NULL - con_name = C_NULL - return solveMCP(f_eval, lb, ub, var_name, con_name) -end - -function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name) - con_name = C_NULL - return solveMCP(f_eval, lb, ub, var_name, con_name) -end - -function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name, con_name) +function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) j_eval = x -> ForwardDiff.jacobian(f_eval, x) return solveMCP(f_eval, j_eval, lb, ub, var_name, con_name) end -function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector) - var_name = C_NULL - con_name = C_NULL - return solveMCP(f_eval, j_eval, lb, ub, var_name, con_name) -end - -function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, var_name) - con_name = C_NULL - return solveMCP(f_eval, j_eval, lb, ub, var_name, con_name) -end - - - -function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, var_name, con_name) +function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) global user_f = f_eval global user_j = j_eval From e29f4daa4350eeec1aeab67d4cd43ebf717d891c Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 17:13:19 -0500 Subject: [PATCH 2/8] use a closure rather than global variable for user callbacks --- src/PATHSolver.jl | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index 0a46314..50f78e9 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -21,11 +21,8 @@ function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con end function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) - global user_f = f_eval - global user_j = j_eval - - f_user_cb = cfunction(f_user_wrap, Cint, (Cint, Ptr{Cdouble}, Ptr{Cdouble})) - j_user_cb = cfunction(j_user_wrap, Cint, (Cint, Cint, Ptr{Cdouble}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cdouble})) + f_user_cb = cfunction(f_user_wrap(f_eval), Cint, (Cint, Ptr{Cdouble}, Ptr{Cdouble})) + j_user_cb = cfunction(j_user_wrap(j_eval), Cint, (Cint, Cint, Ptr{Cdouble}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cdouble})) n = length(lb) z = copy(lb) @@ -85,25 +82,31 @@ end # static int (*j_eval)(int n, int nnz, double *z, int *col_start, int *col_len, # int *row, double *data); -function f_user_wrap(n::Cint, z::Ptr{Cdouble}, f::Ptr{Cdouble}) - F = user_f(unsafe_wrap(Array{Float64}, z, Int(n), false)) - unsafe_store_vector!(f, F) - return Cint(0) +function f_user_wrap(user_f::Function) + function callback(n::Cint, z::Ptr{Cdouble}, f::Ptr{Cdouble}) + F = user_f(unsafe_wrap(Array{Float64}, z, Int(n), false)) + unsafe_store_vector!(f, F) + return Cint(0) + end + return callback end -function j_user_wrap(n::Cint, nnz::Cint, z::Ptr{Cdouble}, - col_start::Ptr{Cint}, col_len::Ptr{Cint}, row::Ptr{Cint}, data::Ptr{Cdouble}) +function j_user_wrap(user_j::Function) + function callback(n::Cint, nnz::Cint, z::Ptr{Cdouble}, + col_start::Ptr{Cint}, col_len::Ptr{Cint}, row::Ptr{Cint}, data::Ptr{Cdouble}) - J = user_j(unsafe_wrap(Array{Float64}, z, Int(n), false)) + J = user_j(unsafe_wrap(Array{Float64}, z, Int(n), false)) - s_col, s_len, s_row, s_data = sparse_matrix(J) + s_col, s_len, s_row, s_data = sparse_matrix(J) - unsafe_store_vector!(col_start, s_col) - unsafe_store_vector!(col_len, s_len) - unsafe_store_vector!(row, s_row) - unsafe_store_vector!(data, s_data) + unsafe_store_vector!(col_start, s_col) + unsafe_store_vector!(col_len, s_len) + unsafe_store_vector!(row, s_row) + unsafe_store_vector!(data, s_data) - return Cint(0) + return Cint(0) + end + return callback end ############################################################################### From d0c2a9c7df11461bba0ca5f18b93de5582ffce93 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 20:15:09 -0500 Subject: [PATCH 3/8] use const Ref to make user functions type-stable --- REQUIRE | 1 + src/PATHSolver.jl | 43 +++++++++++++++++++++---------------------- test/runtests.jl | 28 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/REQUIRE b/REQUIRE index 6d6d039..e219eaa 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,3 +1,4 @@ julia 0.5 ForwardDiff 0.2 BinDeps +FunctionWrappers 0.1 diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index 50f78e9..a60fe81 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -1,6 +1,7 @@ module PATHSolver using ForwardDiff +using FunctionWrappers: FunctionWrapper if isfile(joinpath(dirname(dirname(@__FILE__)), "deps", "deps.jl")) include(joinpath(dirname(dirname(@__FILE__)), "deps", "deps.jl")) @@ -13,7 +14,8 @@ end export solveMCP, options - +const user_f = Ref(FunctionWrapper{Vector{Cdouble}, Tuple{Vector{Cdouble}}}(identity)) +const user_j = Ref(FunctionWrapper{SparseMatrixCSC{Cdouble, Cint}, Tuple{Vector{Cdouble}}}(identity)) function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) j_eval = x -> ForwardDiff.jacobian(f_eval, x) @@ -21,8 +23,10 @@ function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con end function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) - f_user_cb = cfunction(f_user_wrap(f_eval), Cint, (Cint, Ptr{Cdouble}, Ptr{Cdouble})) - j_user_cb = cfunction(j_user_wrap(j_eval), Cint, (Cint, Cint, Ptr{Cdouble}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cdouble})) + user_f[] = f_eval + user_j[] = j_eval + f_user_cb = cfunction(f_user_wrap, Cint, (Cint, Ptr{Cdouble}, Ptr{Cdouble})) + j_user_cb = cfunction(j_user_wrap, Cint, (Cint, Cint, Ptr{Cdouble}, Ptr{Cint}, Ptr{Cint}, Ptr{Cint}, Ptr{Cdouble})) n = length(lb) z = copy(lb) @@ -82,31 +86,26 @@ end # static int (*j_eval)(int n, int nnz, double *z, int *col_start, int *col_len, # int *row, double *data); -function f_user_wrap(user_f::Function) - function callback(n::Cint, z::Ptr{Cdouble}, f::Ptr{Cdouble}) - F = user_f(unsafe_wrap(Array{Float64}, z, Int(n), false)) - unsafe_store_vector!(f, F) - return Cint(0) - end - return callback + +function f_user_wrap(n::Cint, z::Ptr{Cdouble}, f::Ptr{Cdouble}) + F = user_f[](unsafe_wrap(Array{Cdouble}, z, Int(n), false)) + unsafe_store_vector!(f, F) + return Cint(0) end -function j_user_wrap(user_j::Function) - function callback(n::Cint, nnz::Cint, z::Ptr{Cdouble}, - col_start::Ptr{Cint}, col_len::Ptr{Cint}, row::Ptr{Cint}, data::Ptr{Cdouble}) +function j_user_wrap(n::Cint, nnz::Cint, z::Ptr{Cdouble}, + col_start::Ptr{Cint}, col_len::Ptr{Cint}, row::Ptr{Cint}, data::Ptr{Cdouble}) - J = user_j(unsafe_wrap(Array{Float64}, z, Int(n), false)) + J = user_j[](unsafe_wrap(Array{Cdouble}, z, Int(n), false)) - s_col, s_len, s_row, s_data = sparse_matrix(J) + s_col, s_len, s_row, s_data = sparse_matrix(J) - unsafe_store_vector!(col_start, s_col) - unsafe_store_vector!(col_len, s_len) - unsafe_store_vector!(row, s_row) - unsafe_store_vector!(data, s_data) + unsafe_store_vector!(col_start, s_col) + unsafe_store_vector!(col_len, s_len) + unsafe_store_vector!(row, s_row) + unsafe_store_vector!(data, s_data) - return Cint(0) - end - return callback + return Cint(0) end ############################################################################### diff --git a/test/runtests.jl b/test/runtests.jl index 529a674..0834b40 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -93,3 +93,31 @@ status, z, f = solveMCP(elemfunc, jacfunc, lb, ub, var_name, con_name) @test isapprox(z, [2.8, 0.0, 0.8, 1.2]) @test status == :Solved + +function test_in_local_scope() + M = [0 0 -1 -1 ; + 0 0 1 -2 ; + 1 -1 2 -2 ; + 1 2 -2 4 ] + + q = [2; 2; -2; -6] + + myfunc(x) = M*x + q + + n = 4 + lb = zeros(n) + ub = 100*ones(n) + + options(convergence_tolerance=1e-2, output=:no, time_limit=3600) + + status, z, f = solveMCP(myfunc, lb, ub) + + @show status + @show z + @show f + + @test isapprox(z, [2.8, 0.0, 0.8, 1.2]) + @test status == :Solved +end + +test_in_local_scope() From 5602b77e0346e25843b26c0a68da2d330f392467 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 20:38:31 -0500 Subject: [PATCH 4/8] update Travis versions --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f2dd9d5..2ff5c24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ os: - osx julia: - 0.5 + - 0.6 - nightly notifications: email: false From 51a9e2b6872a0b6469401613a03547b3a8f82891 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 20:38:51 -0500 Subject: [PATCH 5/8] use function wrappers for type stability and remove unnecessary code --- src/PATHSolver.jl | 144 ++++++++++++------------------------------ test/runtests.jl | 12 ++++ test/sparse_matrix.jl | 60 ++++++++++++++++++ 3 files changed, 113 insertions(+), 103 deletions(-) create mode 100644 test/sparse_matrix.jl diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index a60fe81..a434c51 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -9,14 +9,17 @@ else error("PATHSolver not properly installed. Please run Pkg.build(\"PATHSolver\")") end - - - export solveMCP, options +# Global function pointers for the user-supplied function and jacobian evaluators. const user_f = Ref(FunctionWrapper{Vector{Cdouble}, Tuple{Vector{Cdouble}}}(identity)) +# The annotated SparseMatrixCSC return type will automatically convert the +# jacobian into the correct sparse form for PATH const user_j = Ref(FunctionWrapper{SparseMatrixCSC{Cdouble, Cint}, Tuple{Vector{Cdouble}}}(identity)) +count_nonzeros(M::AbstractSparseMatrix) = nnz(M) +count_nonzeros(M::AbstractMatrix) = count(x -> !iszero(x), M) # fallback for dense matrices + function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) j_eval = x -> ForwardDiff.jacobian(f_eval, x) return solveMCP(f_eval, j_eval, lb, ub, var_name, con_name) @@ -33,8 +36,7 @@ function solveMCP(f_eval::Function, j_eval::Function, lb::Vector, ub::Vector, va f = zeros(n) J0 = j_eval(z) - s_col, s_len, s_row, s_data = sparse_matrix(J0) - nnz = length(s_data) + nnz = count_nonzeros(J0) t = ccall( (:path_main, "libpath47julia"), Cint, (Cint, Cint, @@ -85,113 +87,49 @@ end # static int (*f_eval)(int n, double *z, double *f); # static int (*j_eval)(int n, int nnz, double *z, int *col_start, int *col_len, # int *row, double *data); - - -function f_user_wrap(n::Cint, z::Ptr{Cdouble}, f::Ptr{Cdouble}) - F = user_f[](unsafe_wrap(Array{Cdouble}, z, Int(n), false)) - unsafe_store_vector!(f, F) - return Cint(0) -end - -function j_user_wrap(n::Cint, nnz::Cint, z::Ptr{Cdouble}, - col_start::Ptr{Cint}, col_len::Ptr{Cint}, row::Ptr{Cint}, data::Ptr{Cdouble}) - - J = user_j[](unsafe_wrap(Array{Cdouble}, z, Int(n), false)) - - s_col, s_len, s_row, s_data = sparse_matrix(J) - - unsafe_store_vector!(col_start, s_col) - unsafe_store_vector!(col_len, s_len) - unsafe_store_vector!(row, s_row) - unsafe_store_vector!(data, s_data) - +function f_user_wrap(n::Cint, z_ptr::Ptr{Cdouble}, f_ptr::Ptr{Cdouble}) + z = unsafe_wrap(Array{Cdouble}, z_ptr, Int(n), false) + f = unsafe_wrap(Array{Cdouble}, f_ptr, Int(n), false) + f .= user_f[](z) return Cint(0) end -############################################################################### - - +function j_user_wrap(n::Cint, expected_nnz::Cint, z_ptr::Ptr{Cdouble}, + col_start_ptr::Ptr{Cint}, col_len_ptr::Ptr{Cint}, + row_ptr::Ptr{Cint}, data_ptr::Ptr{Cdouble}) -############################################################################### -# Converting the Jacobian matrix to the sparse matrix format of the PATH Solver -############################################################################### -function sparse_matrix(A::AbstractSparseArray) - m, n = size(A) - @assert m==n - - col_start = Array{Int}(n) - col_len = Array{Int}(n) - row = Array{Int}(0) - data = Array{Float64}(0) - for j in 1:n - if j==1 - col_start[j] = 1 - else - col_start[j] = col_start[j-1] + col_len[j-1] - end - - col_len[j] = 0 - for i in 1:n - if A[i,j] != 0.0 - col_len[j] += 1 - push!(row, i) - push!(data, A[i,j]) - end - end + z = unsafe_wrap(Array{Cdouble}, z_ptr, Int(n), false) + J::SparseMatrixCSC{Cdouble, Cint} = user_j[](z) + if nnz(J) > expected_nnz + error("Evaluated jacobian has more nonzero entries than were initially provided in solveMCP()") end - return col_start, col_len, row, data -end - -function sparse_matrix(A::Matrix) - return sparse_matrix(sparse(A)) - # m, n = size(A) - # @assert m==n - # - # col_start = Array{Int}(n) - # col_len = Array{Int}(n) - # row = Array{Int}(0) - # data = Array{Float64}(0) - # for j in 1:n - # if j==1 - # col_start[j] = 1 - # else - # col_start[j] = col_start[j-1] + col_len[j-1] - # end - # - # col_len[j] = 0 - # for i in 1:n - # if A[i,j] != 0.0 - # col_len[j] += 1 - # push!(row, i) - # push!(data, A[i,j]) - # end - # end - # end - # - # return col_start, col_len, row, data -end -############################################################################### - - - - - - -function unsafe_store_vector!(x_ptr::Ptr{Cint}, x_val::Vector) - for i in 1:length(x_val) - unsafe_store!(x_ptr, x_val[i], i) + # Transfer data from the computed jacobian into the sparse format that PATH + # expects. Fortunately, PATH uses a compressed-sparse-column storage which + # is compatible with Julia's default SparseMatrixCSC format. + + # col_start in PATH corresponds to J.colptr[1:end-1] + col_start = unsafe_wrap(Array{Cint}, col_start_ptr, Int(n), false) + # col_len in PATH corresponds to diff(J.colptr) + col_len = unsafe_wrap(Array{Cint}, col_len_ptr, Int(n), false) + # row in PATH corresponds to rowvals(J) + row = unsafe_wrap(Array{Cint}, row_ptr, Int(expected_nnz), false) + # data in PATH corresponds to nonzeros(J) + data = unsafe_wrap(Array{Cdouble}, data_ptr, Int(expected_nnz), false) + + @inbounds for i in 1:n + col_start[i] = J.colptr[i] + col_len[i] = J.colptr[i + 1] - J.colptr[i] end - return -end -function unsafe_store_vector!(x_ptr::Ptr{Cdouble}, x_val::Vector) - for i in 1:length(x_val) - unsafe_store!(x_ptr, x_val[i], i) + rv = rowvals(J) + nz = nonzeros(J) + num_nonzeros = nnz(J) + @inbounds for i in 1:num_nonzeros + row[i] = rv[i] + data[i] = nz[i] end - return + return Cint(0) end - - end # Module diff --git a/test/runtests.jl b/test/runtests.jl index 0834b40..7051718 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,8 @@ using PATHSolver using ForwardDiff using Base.Test +include("sparse_matrix.jl") + M = [0 0 -1 -1 ; 0 0 1 -2 ; 1 -1 2 -2 ; @@ -95,6 +97,16 @@ status, z, f = solveMCP(elemfunc, jacfunc, lb, ub, var_name, con_name) @test status == :Solved function test_in_local_scope() + # Verify that we can solve MCPs in local scope. Surprisingly, this is + # is relevant because it affects the way closures are generated. To be + # specific, you can do the following in global scope: + # + # julia> y = [1] + # julia> cfunction(x -> x + y[1], Int, (Int,)) + # + # but running the same code inside a function will fail with: + # ERROR: closures are not yet c-callable + M = [0 0 -1 -1 ; 0 0 1 -2 ; 1 -1 2 -2 ; diff --git a/test/sparse_matrix.jl b/test/sparse_matrix.jl new file mode 100644 index 0000000..8ee479c --- /dev/null +++ b/test/sparse_matrix.jl @@ -0,0 +1,60 @@ +using PATHSolver +using Base.Test + + +# Verify that pulling data directly out of the SparseMatrixCSC form gives +# exactly what the previous hand-coded algorithm gave. +@testset "sparse matrix" begin + function sparse_matrix_reference(A::AbstractSparseMatrix) + m, n = size(A) + @assert m==n + + col_start = Array{Int}(n) + col_len = Array{Int}(n) + row = Array{Int}(0) + data = Array{Float64}(0) + for j in 1:n + if j==1 + col_start[j] = 1 + else + col_start[j] = col_start[j-1] + col_len[j-1] + end + + col_len[j] = 0 + for i in 1:n + if A[i,j] != 0.0 + col_len[j] += 1 + push!(row, i) + push!(data, A[i,j]) + end + end + end + return col_start, col_len, row, data + end + + sparse_matrix_reference(A::Matrix) = sparse_matrix_reference(sparse(A)) + + function sparse_matrix_csc(A::SparseMatrixCSC) + m, n = size(A) + @assert m==n + col_start = A.colptr[1:end-1] + col_len = diff(A.colptr) + row = rowvals(A) + data = nonzeros(A) + return col_start, col_len, row, data + end + + sparse_matrix_csc(A::Matrix) = sparse_matrix_csc(convert(SparseMatrixCSC, A)) + + srand(42) + for i in 1:100 + n = rand(5:20) + M = sprandn(n, n, 0.1) + @test sparse_matrix_csc(M) == sparse_matrix_reference(M) + + Mf = full(M) + @test sparse_matrix_csc(Mf) == sparse_matrix_reference(Mf) + @test sparse_matrix_csc(Mf) == sparse_matrix_csc(M) + end +end + From 5c49e4008a7991debbad8201c7e05dcf8dea3b9f Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 21:15:30 -0500 Subject: [PATCH 6/8] julia v0.5 doesn't have iszero --- src/PATHSolver.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index a434c51..6160d9a 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -18,7 +18,7 @@ const user_f = Ref(FunctionWrapper{Vector{Cdouble}, Tuple{Vector{Cdouble}}}(iden const user_j = Ref(FunctionWrapper{SparseMatrixCSC{Cdouble, Cint}, Tuple{Vector{Cdouble}}}(identity)) count_nonzeros(M::AbstractSparseMatrix) = nnz(M) -count_nonzeros(M::AbstractMatrix) = count(x -> !iszero(x), M) # fallback for dense matrices +count_nonzeros(M::AbstractMatrix) = count(x -> x != 0, M) # fallback for dense matrices function solveMCP(f_eval::Function, lb::Vector, ub::Vector, var_name=C_NULL, con_name=C_NULL) j_eval = x -> ForwardDiff.jacobian(f_eval, x) From eb13c31ab6aa4e910bad8333090dbada9adc3ee9 Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 21:34:29 -0500 Subject: [PATCH 7/8] compatibility with julia v0.5 through v0.7 --- src/PATHSolver.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index 6160d9a..4f0a44f 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -71,10 +71,15 @@ function remove_option_file() end end +# Needed for compatibility with Julia v0.5 through v0.7 +@static if VERSION < v"0.7.0-" + pairs(x) = [(p[1] => p[2]) for p in x] +end + function options(;kwargs...) opt_file = open("path.opt", "w") println(opt_file, "* Generated by PATHSolver.jl. Do not edit.") - for (key, value) in kwargs + for (key, value) in pairs(kwargs) println(opt_file, key, " ", value) end close(opt_file) From 700436c91d189a7de29584a84863dbcc084a4e0c Mon Sep 17 00:00:00 2001 From: Robin Deits Date: Mon, 18 Dec 2017 21:40:30 -0500 Subject: [PATCH 8/8] use Compat.pairs --- REQUIRE | 1 + src/PATHSolver.jl | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/REQUIRE b/REQUIRE index e219eaa..f403b14 100644 --- a/REQUIRE +++ b/REQUIRE @@ -2,3 +2,4 @@ julia 0.5 ForwardDiff 0.2 BinDeps FunctionWrappers 0.1 +Compat 0.40 diff --git a/src/PATHSolver.jl b/src/PATHSolver.jl index 4f0a44f..119493c 100644 --- a/src/PATHSolver.jl +++ b/src/PATHSolver.jl @@ -2,6 +2,7 @@ module PATHSolver using ForwardDiff using FunctionWrappers: FunctionWrapper +using Compat: pairs if isfile(joinpath(dirname(dirname(@__FILE__)), "deps", "deps.jl")) include(joinpath(dirname(dirname(@__FILE__)), "deps", "deps.jl")) @@ -71,11 +72,6 @@ function remove_option_file() end end -# Needed for compatibility with Julia v0.5 through v0.7 -@static if VERSION < v"0.7.0-" - pairs(x) = [(p[1] => p[2]) for p in x] -end - function options(;kwargs...) opt_file = open("path.opt", "w") println(opt_file, "* Generated by PATHSolver.jl. Do not edit.")