How can I stop a Gurobi solve in JuMP in real time (like Ctrl+C) and still keep the solution?

I am using JuMP with Gurobi.jl to solve a MIP.

In Julia, pressing Ctrl+C usually kills the entire Julia process instead of just stopping Gurobi.

My goal is:

  • Stop the optimization at any arbitrary time (not using MIPGap, TimeLimit, or NodeLimit).
  • Still retrieve the best feasible solution found so far.

I’ve seen that Gurobi supports callbacks via JuMP/MOI. Is it possible to have something like a user interrupt (e.g., pressing a key) that triggers a callback to terminate the solver gracefully?

If yes, could someone show me the minimal working example of how to set this up in Julia?

Thanks!

Please press once.

e.g.

import JuMP, Gurobi, SparseArrays
N = 50 # 754 seconds
Q = 1. * SparseArrays.dropzeros(
    SparseArrays.spdiagm(
        [i => rand(-9:9, N - i) for i in 0:div(N, 2)]...
    )
)
m = JuMP.Model();
JuMP.@variable(m, rand(-7:-3) <= y[1:N] <= rand(3:7));
JuMP.@variable(m, rand(-9:-5) <= x[1:N] <= rand(5:9));
JuMP.@objective(m, Min, (x'Q)y);
JuMP.set_optimizer(m, Gurobi.Optimizer)
JuMP.optimize!(m); JuMP.solution_summary(m)

test:

julia> JuMP.optimize!(m); JuMP.solution_summary(m)
Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - "Debian GNU/Linux 12 (bookworm)")

CPU model: AMD EPYC 7763 64-Core Processor, instruction set [SSE2|AVX|AVX2]
Thread count: 128 physical cores, 256 logical processors, using up to 32 threads

Optimize a model with 0 rows, 100 columns and 0 nonzeros
Model fingerprint: 0x8f14f73d
Model has 927 quadratic objective terms
Coefficient statistics:
  Matrix range     [0e+00, 0e+00]
  Objective range  [0e+00, 0e+00]
  QObjective range [2e+00, 2e+01]
  Bounds range     [3e+00, 9e+00]
  RHS range        [0e+00, 0e+00]

Continuous model is non-convex -- solving as a MIP

Found heuristic solution: objective 1972.0000000
Found heuristic solution: objective -90.0000000
Presolve removed 0 rows and 5 columns
Presolve time: 0.01s
Presolved: 921 rows, 1016 columns, 2763 nonzeros
Found heuristic solution: objective -1365.000000
Variable types: 0 continuous, 1016 integer (1016 binary)

Root relaxation: objective -1.668040e+05, 396 iterations, 0.00 seconds (0.00 work units)

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0 -166804.00    0   95 -1365.0000 -166804.00      -     -    0s
H    0     0                    -31392.00000 -166804.00   431%     -    0s
H    0     0                    -42554.00000 -166804.00   292%     -    0s
H    0     0                    -44102.00000 -166804.00   278%     -    0s
H    0     0                    -45200.00000 -166804.00   269%     -    0s
     0     0 -154413.00    0  127 -45200.000 -154413.00   242%     -    0s
     0     0 -148261.00    0  145 -45200.000 -148261.00   228%     -    0s
     0     0 -148189.50    0  145 -45200.000 -148189.50   228%     -    0s
     0     2 -148189.50    0  145 -45200.000 -148189.50   228%     -    0s
H   31    64                    -46492.00000 -134496.00   189%   132    0s
H   50    64                    -47016.00000 -133618.25   184%   121    0s
H   51    64                    -47256.00000 -133215.88   182%   121    0s
H  511   542                    -48548.00000 -129601.50   167%  82.7    0s
H  545   689                    -48697.00000 -129601.50   166%  82.5    0s
H  572   689                    -49412.00000 -129601.50   162%  82.1    0s
* 1639  1572              40    -50381.00000 -128661.00   155%  69.6    0s
* 1640  1572              40    -50573.00000 -128661.00   154%  69.6    0s
* 1642  1572              38    -54946.00000 -128661.00   134%  69.5    0s
H 1794  1693                    -57200.00000 -128596.63   125%  69.3    0s
H 1990  1823                    -57215.00000 -128479.25   125%  69.1    0s
H 1994  1823                    -57981.00000 -128479.25   122%  69.1    0s
H 2020  1823                    -58562.00000 -128479.25   119%  69.0    0s
H 2083  1823                    -59088.00000 -128479.25   117%  69.1    0s
H 2160  1985                    -60250.00000 -128479.25   113%  69.2    0s
H 2340  2000                    -60908.00000 -127924.75   110%  69.3    0s
H 2755  2220                    -62933.00000 -127388.67   102%  68.3    0s
H 3141  2478                    -63149.00000 -126541.00   100%  67.1    0s
 58996 26304 -84114.500   28  813 -63149.000 -94544.362  49.7%  42.3    5s
^C
Cutting planes:
  Gomory: 42
  Flow cover: 340
  Zero half: 366
  RLT: 7
  BQP: 4

Explored 59003 nodes (2510577 simplex iterations) in 7.97 seconds (10.92 work units)
Thread count was 32 (of 256 available processors)

Solution count 10: -63149 -62933 -60908 ... -54946

Solve interrupted
Best objective -6.314900000000e+04, best bound -8.731000000000e+04, gap 38.2603%

User-callback calls 119146, time in user-callback 0.02 sec
solution_summary(; result = 1, verbose = false)
├ solver_name          : Gurobi
├ Termination
│ ├ termination_status : INTERRUPTED
│ ├ result_count       : 10
│ ├ raw_status         : Optimization was terminated by the user.
│ └ objective_bound    : -8.73100e+04
├ Solution (result = 1)
│ ├ primal_status        : FEASIBLE_POINT
│ ├ dual_status          : NO_SOLUTION
│ ├ objective_value      : -6.31490e+04
│ └ relative_gap         : 3.82603e-01
└ Work counters
  ├ solve_time (sec)   : 7.99659e+00
  ├ simplex_iterations : 2510577
  ├ barrier_iterations : 0
  └ node_count         : 59003

julia> JuMP.value.(x)
50-element Vector{Float64}:

This is not correct either, you can refer to The Julia REPL · The Julia Language

no, it is not working. and i want to add one more thing, i’m using server base gurobi license, not on local machine.

I think the requirement is somewhat unusual.
But you can use GRBterminate(backend(model)) within a callback, as written in GitHub - jump-dev/Gurobi.jl: A Julia interface to the Gurobi Optimizer.
As for achieving your “arbitrary” goal…
Since I have no idea what a cloud solve is like… But in julia maybe you can try some asynchronous programming thing:

import JuMP, Gurobi, SparseArrays
N = 50 # 754 seconds
Q = 1. * SparseArrays.dropzeros(
    SparseArrays.spdiagm(
        [i => rand(-9:9, N - i) for i in 0:div(N, 2)]...
    )
);
m = JuMP.direct_model(Gurobi.Optimizer())
JuMP.@variable(m, rand(-7:-3) <= y[1:N] <= rand(3:7));
JuMP.@variable(m, rand(-9:-5) <= x[1:N] <= rand(5:9));
JuMP.@objective(m, Min, (x'Q)y);

# Run the solve async-ly
Threads.@spawn JuMP.optimize!(m)

# when you want to interrupt, execute this command in the REPL
Gurobi.GRBterminate(JuMP.backend(m))

# you can check the current status
JuMP.solution_summary(m)
JuMP.value.(x)

Hi @amit_97, welcome to the forum :smile:

i’m using server base gurobi license, not on local machine.

Do you mean the Gurobi instant cloud? Or just the WLS license?

If you’re using the Gurobi instant cloud, the Gurobi documentation sounds like it should support termination, so if CTRL+C isn’t working, then that is a bug we should fix: Callbacks - Gurobi Remote Services Guide

Do you have a reproducible example of your model? What options did you set? (You shouldn’t are any sensitive API keys, etc.)

We trap the interrupt and call GRBterminate: Gurobi.jl/src/MOI_wrapper/MOI_callbacks.jl at 19882cf814f5438dc8a339c06f7f2c416612b156 · jump-dev/Gurobi.jl · GitHub

Let’s please wait for Amit to reply.

Hello @odow, thank you for your response. However, I’m experiencing an issue specifically with VS Code and its integrated terminal. When I run the code through the VS Code terminal, it doesn’t accept any input or respond until Gurobi finishes solving. In contrast, when I execute the same code via PowerShell, the “Ctrl + C” interrupt command works as expected.

property of VS Code terminal:
Command line: C:\Users\xyz\AppData\Local\Programs\Julia-1.10.9\bin\julia.exe
-i --banner=no --project=C:\Users\xyz.julia\environments\v1.10 c:\Users\xyz.cursor\extensions\julialang.language-julia-1.149.2-universal\scripts\terminalserver\terminalserver.jl
.\pipe\vsc-jl-repl-e177509b-df29-4d52-9441-08472c4f840a
.\pipe\vsc-jl-repldbg-033bc92e-4781-4c7e-b229-9f8308e2fccb
.\pipe\vsc-jl-cr-d830131c-4d65-4ce2-b12f-a2372fab5eac USE_REVISE=true
USE_PLOTPANE=true USE_PROGRESS=true
ENABLE_SHELL_INTEGRATION=true
DEBUG_MODE=false

property of PowerShell terminal:

Command line: C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
-noexit -command ‘try { . “c:\Users\xyz\AppData\Local\Programs\cursor\resources\app\out\vs\workbench\contrib\terminal\common\scripts\shellIntegration.ps1” } catch {}’