Thank you so much for the help! I have not really dealt with anything related to C before, so I would never have guessed how to use this syntax on my own.
So, it runs now, but I am a bit in doubt about the output I get.
My function now looks the following way:
function my_callback_function(cb_data::CPLEX.CallbackContext, context_id::Clong)
if context_id != CPX_CALLBACKCONTEXT_BRANCHING
@info "callback called"
# Before querying `callback_value`, you must call:
CPLEX.load_callback_variable_primal(cb_data, context_id)
# get the relevant values from the callback
y_val = callback_value.(cb_data, m[:y])
xp_val = callback_value.(cb_data, m[:xp])
xn_val = callback_value.(cb_data, m[:xn])
b_val = callback_value.(cb_data, m[:b])
#Compute the maximum distance to the ReLU hyperplane
d = (.-xn_val.+xp_val.+1)./(y_val.+1) #add the '+1' to avoid division by zero
#get the index of the associated fractional binary (if the ReLU is not enforced it will be > 1 and otherwise = 1)
b_chs = argmax(d)
#load the variable i want to branch on with proper CPLEX syntax:
column = CPLEX.column(cb_data, index(m[:b][b_chs]))
status = CPXcallbackmakebranch(
cb_data,
1, # varcnt
Cint[column], # varind
Cchar['U'], # varlu
Cdouble[1.0], # varbd #need to make two child nodes...
0, # rcnt
0, # nzcnt
Cdouble[], # rhs
Cchar[], # sense
Cint[], # rmatbeg
Cint[], # rmatind
Cdouble[], # rmatval
503, # nodeest TODO: what is an objective estimate for the branch? -> i set the objective to 503 as that is slightly above the objective of the root node (and i know the solution is around there)
seqnum_p, # seqnum_p
)
if status != 0
@warn "CPXcallbackmakebranch failed with status $(status)"
else
@info "I submitted a new branching strategy that branches on $(b_chs), and the status was: $(status) (successful)"
end
end
MOI.set(m, MOI.NumberOfThreads(), 1) #It crashed when running multithread...
MOI.set(m, CPLEX.CallbackFunction(), my_callback_function) #setting the callback function through MOI
The info logs that are outputted keep repeating until CPLEX does a restart (which CPLEX just does every once in a while I guess?) Do you know if that is expected behavior, or does that mean it is not branching the way I expect it to?
Version identifier: 20.1.0.0 | 2020-11-10 | 9bedb6d68
CPXPARAM_Threads 1
Generic callback 0xfe
Tried aggregator 3 times.
MIP Presolve eliminated 1930 rows and 202 columns.
Aggregator did 13110 substitutions.
Reduced MIP has 16013 rows, 16304 columns, and 55651 nonzeros.
Reduced MIP has 4987 binaries, 0 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.05 sec. (50.47 ticks)
Probing fixed 673 vars, tightened 8265 bounds.
Probing time = 0.08 sec. (41.28 ticks)
Tried aggregator 1 time.
MIP Presolve eliminated 1346 rows and 1346 columns.
MIP Presolve modified 8060 coefficients.
Reduced MIP has 14667 rows, 14958 columns, and 51782 nonzeros.
Reduced MIP has 4314 binaries, 0 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.02 sec. (15.07 ticks)
Probing fixed 12 vars, tightened 2476 bounds.
Probing time = 0.08 sec. (37.24 ticks)
Clique table members: 5464.
MIP emphasis: balance optimality and feasibility.
MIP search method: dynamic search.
Parallel mode: none, using 1 thread.
Root relaxation solution time = 4.61 sec. (4736.57 ticks)
Nodes Cuts/
Node Left Objective IInf Best Integer Best Bound ItCnt Gap
0 0 502.5709 1126 502.5709 16619
0 0 502.5709 504 Cuts: 2182 18094
0 0 502.5709 590 Cuts: 2567 20193
0 0 502.5709 364 Cuts: 771 20862
0 0 502.5709 392 Cuts: 1245 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
0 1 502.5709 274 502.5709 21631
Elapsed time = 44.69 sec. (44797.98 ticks, tree = 0.01 MB, solutions = 0)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
... # it repeats a lot in between
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
20 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
...
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
60 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
...
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
200 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
...
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
427 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
...
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
650 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
...
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
937 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
2110 1 502.5709 274 502.5709 21631
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
[ Info: I submitted a new branching strategy that branches on CartesianIndex(10, 20, 2, 5), and the status was: 0 (successful)
# Then it performs a restart...
Performing restart 1
Repeating presolve.
Tried aggregator 2 times.
MIP Presolve eliminated 24 rows and 24 columns.
MIP Presolve modified 3548 coefficients.
Aggregator did 3 substitutions.
Reduced MIP has 14640 rows, 14931 columns, and 51282 nonzeros.
Reduced MIP has 4302 binaries, 0 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.02 sec. (21.43 ticks)
Tried aggregator 1 time.
MIP Presolve eliminated 92 rows and 92 columns.
MIP Presolve modified 593 coefficients.
Reduced MIP has 14548 rows, 14839 columns, and 51052 nonzeros.
Reduced MIP has 4256 binaries, 0 generals, 0 SOSs, and 0 indicators.
Presolve time = 0.02 sec. (14.91 ticks)
Represolve time = 0.17 sec. (82.43 ticks)
2116 0 502.5709 1770 Cuts: 3737 50313
2116 0 502.5709 701 Cuts: 3737 52719
2116 0 502.5709 771 Cuts: 3737 54692
2116 0 502.5709 1282 Cuts: 3737 67158
2116 0 502.5709 820 Cuts: 3737 69958
2116 0 502.5709 772 Cuts: 3737 72132
[ Info: I submitted a new branching strategy that branches on CartesianIndex(12, 20, 2, 5), and the status was: 0 (successful)
... #after restart it finds a new variable
Also, since the variables that I am branching on are binary, I want to create two child nodes. One where it is set to 0 and the other to 1. Should I explicitly tell CPLEX to make two child nodes, by doing something like this:
seqnum_p = Ref{Cint}(0)
status = CPXcallbackmakebranch(
cb_data,
2, # varcnt
Cint[column, column], # varind UPDATE: added the same binary (column) twice, one call for upper and one for lower bound.
Cchar['L', 'U'], # varlu UPDATE: added 'L'
Cdouble[0.0, 1.0], # varbd UPDATE: ADDED 0 to the lower bound
0, # rcnt
0, # nzcnt
Cdouble[], # rhs
Cchar[], # sense
Cint[], # rmatbeg
Cint[], # rmatind
Cdouble[], # rmatval
503, # nodeest
seqnum_p, # seqnum_p
)
or does it already implicitly know that it should create the other child node, or is there something completely different I need to do?
Last thing, which is a bit off-topic. Do you know if I can supply CPLEX with a partial solution to some binaries, together with the branching strategy? due to the way the mixed integer constraints are made, I know that a few (maybe 10-15 out of all the fractional binary variables) could be set to integer values, as they already satisfy the associated constraints (but the solver does not explicitly know this when they reach the MIPnode). I did that previously in Gurobi using a Heuristic callback, which seemed to work, but I donât know if can both provide a solver-independent heuristic callback to CPLEX on top of the solver-dependent branching callback that I am implementing now, but maybe CPLEX has some other way to do it?
Either way, thank you SO MUCH for your help so far, itâs highly appreciated!