How to obtain the fracGap or gapRel from a solution of PuLP?

1.3k Views Asked by At

I am using python Pulp package to solve linear programs (MILPs as well). I find that certain problems are taking a lot of time to reach optimality and so I am forced to use a time limit on the solver.

answer=prob.solve(p.PULP_CBC_CMD(timeLimit=99))

I need to save the value of the relative %-age fractional gap "gapRel" into a file for later use. Please suggest ways to print it separately.

Presently I am copying the Fractional Gap from the Solutions printed by the PuLP Solver and pasting it in Notepad manually for later reference; but I wish to automate this process and therefore need to obtain the fractional gap value into a variable.

1

There are 1 best solutions below

0
On

Late answer here.

This problem is actually non-trivial since COIN CBC (PuLP's default solver) doesn't output the gap or the lower bound of the MIP, they are only displayed in its logs and cannot be accessed otherwise.

The solution is thus to examine those logs and to retrieve the desired values ! Fortunately, @pchtsp (which can be found in the above comments) has developped a library dedicated to this need : orloge.

The code writes as follows :

"""Code tested for PuLP 2.7.0 and orloge 0.17.2"""
import orloge # pip install orloge
import pulp as pl

path = "path_to_log_file.txt"
 
solver = pl.PULP_CBC_CMD(msg=False, timeLimit=99, logPath=path) # We set msg=False since logPath disables CBC's logs in the Python console
answer = prob.solve(solver)

logs_dict = orloge.get_info_solver(path, 'CBC') # Orloge returns a dict with all logs info
best_bound, best_solution = logs_dict["best_bound"], logs_dict["best_solution"]

if best_bound is not None and best_solution is not None :
    gap = abs(best_solution - best_bound) / (1e-10 + abs(best_bound)) * 100 # Gap in %. We add 1e-10 to avoid division by zero.
    print(f"Gap (relative to the lower bound) is {gap:.2f}%.")
else :
    print("Unable to retrieve gap.")

The gap found is relative to the lower bound, which is consistent with CBC's logs in the Python console. Note that orloge's gap (which can be accessed with logs_dict["gap"]) is related to the objective (see calculation here), and is thus different from that of CBC.


The downsides of this method are :

  1. You have to save and read an entire log file only to find the gap, which is quite inefficient.
  2. Doing so disables CBC's logs in Python console.
  3. You rely on an external library. This method can thus become outdated if orloge is not updated simultaneously with CBC.

However, that's the only solution I found, and it's better than nothing. Hope it will be useful.