From b9a0b1d718b96e871d5abe2f56a3e964280737bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20Besan=C3=A7on?= Date: Wed, 2 Nov 2022 16:46:29 +0100 Subject: [PATCH 1/2] relaxation handler --- src/MOI_wrapper.jl | 4 +- src/SCIP.jl | 2 +- src/{cut_selector.jl => plugins.jl} | 69 +++++++++++++++++++++++++++++ src/scip_data.jl | 3 ++ 4 files changed, 75 insertions(+), 3 deletions(-) rename src/{cut_selector.jl => plugins.jl} (76%) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index a59f0c42..eb8eea02 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -41,7 +41,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer @SCIP_CALL SCIPincludeDefaultPlugins(scip[]) @SCIP_CALL SCIP.SCIPcreateProbBasic(scip[], "") - scip_data = SCIPData(scip, Dict(), Dict(), 0, 0, Dict(), Dict(), Dict(), Dict(), []) + scip_data = SCIPData(scip, Dict(), Dict(), 0, 0, Dict(), Dict(), Dict(), Dict(), Dict(), []) o = new(scip_data, PtrMap(), ConsTypeMap(), Dict(), Dict(), Dict(), nothing, MOI.MIN_SENSE) finalizer(free_scip, o) @@ -198,7 +198,7 @@ function MOI.empty!(o::Optimizer) @SCIP_CALL SCIPincludeDefaultPlugins(scip[]) @SCIP_CALL SCIP.SCIPcreateProbBasic(scip[], "") # create a new problem - o.inner = SCIPData(scip, Dict(), Dict(), 0, 0, Dict(), Dict(), Dict(), Dict(), []) + o.inner = SCIPData(scip, Dict(), Dict(), 0, 0, Dict(), Dict(), Dict(), Dict(), Dict(), []) # reapply parameters for pair in o.params set_parameter(o.inner, pair.first, pair.second) diff --git a/src/SCIP.jl b/src/SCIP.jl index f1d54ec2..268890a4 100644 --- a/src/SCIP.jl +++ b/src/SCIP.jl @@ -16,7 +16,7 @@ include("scip_data.jl") include("sepa.jl") # cut selectors -include("cut_selector.jl") +include("plugins.jl") # constraint handlers include("conshdlr.jl") diff --git a/src/cut_selector.jl b/src/plugins.jl similarity index 76% rename from src/cut_selector.jl rename to src/plugins.jl index a4c90fd1..c66e1524 100644 --- a/src/cut_selector.jl +++ b/src/plugins.jl @@ -1,3 +1,6 @@ +# Additional SCIP plugins +# Relaxation handler and cut selection interface + # cut selection interface # it is recommended to check https://scipopt.org/doc/html/CUTSEL.php for key concepts and interface @@ -187,3 +190,69 @@ function select_cuts(cutsel::HybridCutSelector, scip, cuts, forced_cuts, root, m @assert retcode != SCIP_OKAY || nselected_cuts[] >= 0 return (retcode, nselected_cuts[], SCIP_SUCCESS) end + +## Relaxation handlers +# it is recommended to check https://scipopt.org/doc/html/RELAX.php for key concepts and interface + + +abstract type RelaxationHandler end + +""" + compute_relaxation(::RelaxationHandler, scip::Ptr{SCIP_}) -> (retcode, lower_bound, result) + +Compute the relaxation +""" +function compute_relaxation +end + +# (SCIP* scip, SCIP_RELAX* relax, SCIP_Real* lowerbound, SCIP_RESULT* result) +function _compute_relaxation_callback(scip::Ptr{SCIP_}, relax_::Ptr{SCIP_RELAX}, lowerbound_::Ptr{SCIP_Real}, result_::Ptr{SCIP_RESULT}) + relaxdata::Ptr{SCIP_RELAXDATA} = SCIPrelaxGetData(relax_) + relax_handler = unsafe_pointer_to_objref(relaxdata) + (retcode, lower_bound, result) = compute_relaxation(relax_handler, scip)::Tuple{SCIP_RETCODE, Real, SCIP_RESULT} + if retcode != SCIP_OKAY + return retcode + end + unsafe_store!(lowerbound_, Cdouble(lower_bound)) + unsafe_store!(result_, result) + return retcode +end + +function _relaxfree(::Ptr{SCIP_}, relax::Ptr{SCIP_RELAX}) + # just like sepa, free the data on the SCIP side, + # the Julia GC will take care of the objects + SCIPrelaxSetData(relax, C_NULL) + return SCIP_OKAY +end + +""" +Includes a relaxator in SCIP and stores it in relaxator_storage. +""" +function include_relaxator(scip::Ptr{SCIP_}, relax::REL, relax_storage::Dict{Any, Ptr{SCIP_RELAX}}; name = "", description = "", priority=10000, frequency=0) where {REL <: RelaxationHandler} + + # ensure a unique name for the cut selector + if name == "" + name = "relaxator_$(string(REL))" + end + + relax__ = Ref{Ptr{SCIP_RELAX}}(C_NULL) + if !ismutable(relax) + throw(ArgumentError("The relaxation handler structure must be a mutable type")) + end + + relaxdata_ = pointer_from_objref(relax) + relax_callback = @cfunction( + _compute_relaxation_callback, SCIP_RETCODE, + (Ptr{SCIP_}, Ptr{SCIP_RELAX}, Ptr{SCIP_Real}, Ptr{SCIP_RESULT}), + ) + @SCIP_CALL SCIPincludeRelaxBasic(scip, relax__, name, description, priority, frequency, relax_callback, relaxdata_) + @assert relax__[] != C_NULL + + @SCIP_CALL SCIPsetRelaxFree( + scip, relax__[], + @cfunction(_relaxfree, SCIP_RETCODE, (Ptr{SCIP_}, Ptr{SCIP_RELAX})), + ) + + # store relaxator (avoids GC-ing it) + relax_storage[relax] = relax__[] +end diff --git a/src/scip_data.jl b/src/scip_data.jl index 4f1221d0..f5bbe355 100644 --- a/src/scip_data.jl +++ b/src/scip_data.jl @@ -46,6 +46,9 @@ mutable struct SCIPData # User-defined cut selector cutsel_storage::Dict{Any, Ptr{SCIP_CUTSEL}} + # User-defined relaxation handlers + relax_storage::Dict{Any, Ptr{SCIP_RELAX}} + # to store expressions for release nonlinear_storage::Vector{NonlinExpr} end From 3f5248bd27c790e391ef2516192cf2d784109723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20Besan=C3=A7on?= Date: Wed, 2 Nov 2022 17:13:20 +0100 Subject: [PATCH 2/2] documentation --- src/MOI_wrapper/conshdlr.jl | 4 ++++ src/plugins.jl | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/MOI_wrapper/conshdlr.jl b/src/MOI_wrapper/conshdlr.jl index efaeaa57..f10b3ed5 100644 --- a/src/MOI_wrapper/conshdlr.jl +++ b/src/MOI_wrapper/conshdlr.jl @@ -71,3 +71,7 @@ end function include_cutsel(o::Optimizer, cutsel::CS; name = "", description = "", priority=10000) where {CS <: AbstractCutSelector} return include_cutsel(o.inner.scip[], cutsel, o.inner.cutsel_storage; name=name, description=description, priority=priority) end + +function include_relaxator(o::Optimizer, relax::REL; name = "", description = "", priority=10000, frequency=0) where {REL <: RelaxationHandler} + return include_relaxator(o.inner.scip[], relax; name=name, descriptio=description, priority=priority, frequency=frequency) +end diff --git a/src/plugins.jl b/src/plugins.jl index c66e1524..e5da523b 100644 --- a/src/plugins.jl +++ b/src/plugins.jl @@ -200,12 +200,12 @@ abstract type RelaxationHandler end """ compute_relaxation(::RelaxationHandler, scip::Ptr{SCIP_}) -> (retcode, lower_bound, result) -Compute the relaxation +Compute a relaxation at the current node (with local bounds). """ function compute_relaxation end -# (SCIP* scip, SCIP_RELAX* relax, SCIP_Real* lowerbound, SCIP_RESULT* result) +# Low-level function matching the C signature function _compute_relaxation_callback(scip::Ptr{SCIP_}, relax_::Ptr{SCIP_RELAX}, lowerbound_::Ptr{SCIP_Real}, result_::Ptr{SCIP_RESULT}) relaxdata::Ptr{SCIP_RELAXDATA} = SCIPrelaxGetData(relax_) relax_handler = unsafe_pointer_to_objref(relaxdata)