Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

relaxation handler #251

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/MOI_wrapper/conshdlr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/SCIP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
69 changes: 69 additions & 0 deletions src/cut_selector.jl → src/plugins.jl
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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 a relaxation at the current node (with local bounds).
"""
function compute_relaxation
end

# 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)
(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
3 changes: 3 additions & 0 deletions src/scip_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down