From 41f9234de1b872d9cb77aed677ad97f2b28222b2 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Mon, 23 Jan 2023 14:19:33 -0600 Subject: [PATCH] CpxCuts: Use sparse arrays in H5 file --- Project.toml | 3 +++ deps/formatter/Project.toml | 5 ---- deps/formatter/format.jl | 8 ------ src/Cuts/BlackBox/cplex.jl | 48 +++++++++++++++++------------------- src/MIPLearn.jl | 40 ++++++++++++++++++++++++++++++ test/fixtures/bell5.h5 | Bin 67206 -> 79074 bytes 6 files changed, 66 insertions(+), 38 deletions(-) delete mode 100644 deps/formatter/Project.toml delete mode 100644 deps/formatter/format.jl diff --git a/Project.toml b/Project.toml index ecfb5dd..4e69f77 100644 --- a/Project.toml +++ b/Project.toml @@ -5,9 +5,12 @@ version = "0.3.0" [deps] CPLEX = "a076750e-1247-5638-91d2-ce28b192dca0" +Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] JuMP = "1" diff --git a/deps/formatter/Project.toml b/deps/formatter/Project.toml deleted file mode 100644 index 4bc5f25..0000000 --- a/deps/formatter/Project.toml +++ /dev/null @@ -1,5 +0,0 @@ -[deps] -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" - -[compat] -JuliaFormatter = "0.14.4" diff --git a/deps/formatter/format.jl b/deps/formatter/format.jl deleted file mode 100644 index 9f9d965..0000000 --- a/deps/formatter/format.jl +++ /dev/null @@ -1,8 +0,0 @@ -using JuliaFormatter -format( - [ - "../../src", - "../../test", - ], - verbose=true, -) diff --git a/src/Cuts/BlackBox/cplex.jl b/src/Cuts/BlackBox/cplex.jl index 796dea3..5324dc6 100644 --- a/src/Cuts/BlackBox/cplex.jl +++ b/src/Cuts/BlackBox/cplex.jl @@ -36,13 +36,13 @@ function collect( CPXsetintparam(env, CPX_PARAM_NODELIM, 0) # Parameter: Make cutting plane generation more aggresive - CPXsetintparam(env, CPX_PARAM_AGGCUTLIM, 100) - CPXsetintparam(env, CPX_PARAM_FRACCAND, 1000) CPXsetintparam(env, CPX_PARAM_FRACCUTS, 2) - CPXsetintparam(env, CPX_PARAM_FRACPASS, 100) - CPXsetintparam(env, CPX_PARAM_GUBCOVERS, 100) CPXsetintparam(env, CPX_PARAM_MIRCUTS, 2) CPXsetintparam(env, CPX_PARAM_ZEROHALFCUTS, 2) + # CPXsetintparam(env, CPX_PARAM_AGGCUTLIM, 100) + # CPXsetintparam(env, CPX_PARAM_FRACCAND, 1000) + # CPXsetintparam(env, CPX_PARAM_FRACPASS, 100) + # CPXsetintparam(env, CPX_PARAM_GUBCOVERS, 100) # Load problem lp = CPXcreateprob(env, status_p, "problem") @@ -70,37 +70,35 @@ function collect( # Load generated MPS file model = JuMP.read_from_file("$tempdir/root.mps") + function select(cr) + return name(cr)[begin] in ['i', 'f', 'm', 'r', 'L', 'z', 'v'] && isdigit(name(cr)[begin+1]) + end + # Parse cuts - cuts_lhs::Vector{Vector{Float64}} = [] - cuts_rhs::Vector{Float64} = [] - nvars = num_variables(model) constraints = all_constraints(model, GenericAffExpr{Float64,VariableRef}, MOI.LessThan{Float64}) + nvars = num_variables(model) + ncuts = length([cr for cr in constraints if select(cr)]) + cuts_lhs = spzeros(ncuts, nvars) + cuts_rhs = Float64[] + + offset = 1 for conRef in constraints - if name(conRef)[begin] in ['i', 'f', 'm', 'r', 'L', 'z', 'v'] && - isdigit(name(conRef)[begin+1]) + if select(conRef) c = constraint_object(conRef) cset = MOI.get(conRef.model.moi_backend, MOI.ConstraintSet(), conRef.index) - lhs = zeros(nvars) for (key, val) in c.func.terms - lhs[key.index.value] = val + cuts_lhs[offset, key.index.value] = val end - push!(cuts_lhs, lhs) push!(cuts_rhs, cset.upper) + offset += 1 end end - @info "$(length(cuts_lhs)) CPLEX cuts collected" - cuts_lhs_matrix::Matrix{Float64} = vcat(cuts_lhs'...) - - # Store cuts in HDF5 file - h5open(h5_filename, "r+") do h5 - for key in ["cuts_cpx_lhs", "cuts_cpx_rhs"] - if haskey(h5, key) - delete_object(h5, key) - end - end - write(h5, "cuts_cpx_lhs", cuts_lhs_matrix) - write(h5, "cuts_cpx_rhs", cuts_rhs) - end + + @info "Storing $(length(cuts_rhs)) CPLEX cuts..." + h5 = Hdf5Sample(h5_filename) + h5.put_sparse("cuts_cpx_lhs", cuts_lhs) + h5.put_array("cuts_cpx_rhs", cuts_rhs) + h5.file.close() return end diff --git a/src/MIPLearn.jl b/src/MIPLearn.jl index da04172..b2d5b95 100644 --- a/src/MIPLearn.jl +++ b/src/MIPLearn.jl @@ -4,6 +4,46 @@ module MIPLearn +using PyCall +using SparseArrays + +global miplearn = PyNULL() +global Hdf5Sample = PyNULL() + +to_str_array(values) = py"to_str_array"(values) + +from_str_array(values) = py"from_str_array"(values) + +function __init__() + copy!(miplearn, pyimport("miplearn")) + copy!(Hdf5Sample, miplearn.features.sample.Hdf5Sample) + + py""" + import numpy as np + + def to_str_array(values): + if values is None: + return None + return np.array(values, dtype="S") + + def from_str_array(values): + return [v.decode() for v in values] + """ +end + +function convert(::Type{SparseMatrixCSC}, o::PyObject) + I, J, V = pyimport("scipy.sparse").find(o) + return sparse(I .+ 1, J .+ 1, V, o.shape...) +end + +function PyObject(m::SparseMatrixCSC) + pyimport("scipy.sparse").csc_matrix( + (m.nzval, m.rowval .- 1, m.colptr .- 1), + shape = size(m), + ).tocoo() +end + + include("Cuts/BlackBox/cplex.jl") end # module \ No newline at end of file diff --git a/test/fixtures/bell5.h5 b/test/fixtures/bell5.h5 index 6ccec97ef63ad54c7ebd3593faf5198c4b8a361e..1741af84255afaf86a2cd8fd079883a3c4341c91 100644 GIT binary patch delta 2758 zcmb7F30PBC7Jd%`JRm@rO4!1tC_xGmgBFxA0dWJ0`M?FH2!Y7bkz$ZF(2=bw%dp5r z)Uw${Y84wGMT7*B4@6W}6$*+5tpc9x!qD|bIe2Bm70%3po8 z@QyNL^OO-Yo-ZLl4ORjT1{Sa*m_{sshM}oC_s7>C@Alg30zpc^-f$DC(OMdYBJ-5X zZm05xyi~(?pD39*lv{T50-7Jez@J0XdHw~G2Jw|4$PilZUy}j-gbhJF$6jaghu-g@ zX>>}0xL>om(6{JN7YH~rxNR!wS}21|o_|8s2e`@!UKr5H$z?%!R&-RPKZ_mbejPZ$HSU^B?W;|fHf!p z48w9qU670H_=_$Mmb+k{FDzjoMoQOkGn?9END|T0#mHUeDIxp6K7dXwR+ypzO)e3* z^9cr1LMg~^C2GPj&0oY?Cv9aUZ2aX{*K_t)-KU{BTtXJ|v~|r)I)uyN^HkM=fPWM1 zjbMUn1KKJXd8*r%hipKqBA{AZHL;K=T5EpiyL&>kwBdhJ?ao(ql2H@O@>nLy8QnlZ z%G-o;i8O}kHioq<*;jpf<{)Lf5sSMO-HMG!_;mM-5xrd~$;o@u*4Fcxsn}`~xLsw7 z_V7GavMehsv*qjr{0vEI0=`I+0RASHmo~w`@Ta#Am-G6w>dnX>rg$2l;3%hD8L!u9 z>1|=)QAYsbb!6~^KoiU&DWIcHPtRh0>x@@L+|qU(QrPF7$SA^S9zs#P(e6nQ)E8&l zh^l2qQ$qACV2hrJd;zs4qKdumD=E|~guZ=+vz9is!l}qvpU2&07_L1=VD0h=Gk58| zm~%6KiNYq*!OZa+Y;5BT&<#Y?ei6z|Q16py46;m}vP0^Lp0qg_gf}qrRK0l}4Rjbx zHOv7`5khvoh_%`!Y%58+_DgfK^ii#hhX*iPe!xx?n5T*$e@%81trvS@Ix=CbU$8&o zE?anj=SFQH_zA&jQQZHxK*`v`bU8@r(0Efw-xlHT$)kM+2YXq;ON88Zm9AYEfyS9e zY{?7XPe1rLkS(NiI~jF59a%E^+d-^d7MfJtjTK&U!)_W{-jnn8h*KHn!B}zkKo`Fq zcr=qicFP6HA0SWl?}XKCurp4UE0^z#bR^dcU2Q_%^ieQsbVN=Kg(V|i7Q1L4bvjG0 zztP}1bKg`8%q$6PxMPdd_172O8g^aHY2qFk*tF*Yk5+Px4Gw+>qwF@qh-|}YSQK=f zjOZjp9NoTe!+YQ~`M<>qPK@(oF&Xx^L?U)l^595-va*ILKVY%x89jthAKiKDEgt_5 zdgYy+HjbVudIo*jwn@oNPPnHPCi=ZsloUo4RkAnUCMiJ3I#My3uBn?y(VLNz=A{QLwbU)wmWVZWFme92$@(8MAuYDzNZXdlPd&sz4%U?VC)XS{ zRrZw4=7-ELax`yW_n3V%znztxu<(){MKlSs=Dz0CTpgUQOnW-%7cVfWb9_!qY zNw1L>y)VxeUGQ?CjtJ6f?GB&FGJJBc_(^M1=^oDJ&cx3LuW_i8b7BkOT$ahZAuZhr zBMt1(hE{|yL;Lna{XOn4tA`hSDzrAd2xcJP>Wl4MNG{az*dNtTU0rueeyq4UZAvEM0zD6J|WjFKmTf>3&Gle_Ta@swj_{4)IJTeS2+Q+xIio;kx=e@wxM+G4#Q^_2