EECE571F-project / B&B / read_mps.py
read_mps.py
Raw
"""
Description: A Python script to read .mps files which contain MILP problems and convert them to numpy format.
"""

import pulp # library used to read .mps files
from pulp import *
import numpy as np
import time
from pulp.constants import LpConstraintEQ, LpConstraintLE, LpConstraintGE
from branch_and_bound import BnB

write_flag = False # change to True if you want to write .json and .lp

# read .mps file
info = pulp.LpProblem.fromMPS("./B&B/22433.mps") # read .mps file
# The variable 'info' is a tuple of size 2. 
# - The first entry, info[0] holds a dict of variable names
# - The second entry, info[1] holds a pulp.LpProblem

lp_problem = info[1]

# Returns a list of pulp.LpVariables 
lp_variables = lp_problem.variables()

# Returns the objective of the LP as a pulp.LpAffineExpression  
lp_objective = lp_problem.objective

# Maximize or Minimization problem
type_lookup = {1: 'minimize', -1: 'maximize'}
print(f"Type: {type_lookup[lp_problem.sense]}")

# Print the name of the LP 
print(f"Problem name: {lp_problem.name}")

if write_flag:
    # Convert to .json format (messy and hard to read)
    lp_problem.toJson("./B&B/example_info.json")

    # Write the LP in .lp format (This is easy for humans to read)
    lp_problem.writeLP("./B&B/example_info.lp")

# If you want you can enumerate all the constraints (I wouln't suggest doing this for large LPs)
# for name, constraint in problem.constraints.items():
#     print(f"{name}: {constraint}")

# Convert the pulp.LpProblem to numpy format
print("Converting .mps to numpy format...")
start = time.time()

# Obtain the coefficients of the objective, i.e. c'x
c = np.array([lp_problem.objective.get(var, 0) for var in lp_problem.variables()])

# Hold constraints
A_lt = [] #Ax <= b
b_lt = []

A_eq = [] # Ax = b
b_eq = []


for constraint in lp_problem.constraints.values():
    row = [constraint.get(var, 0) for var in lp_problem.variables()]  # Coefficients of the constraint
    
    if constraint.sense == LpConstraintEQ: # a'x = b constraint
        A_eq.append(row)
        b_eq.append(-constraint.constant)
        # b_eq.append(constraint.constant)
        # print(f"{constraint.constant}")
    elif constraint.sense == LpConstraintLE: # a'x <= constraint
        A_lt.append(row)
        b_lt.append(-constraint.constant)
        # b_lt.append(constraint.constant)
    elif constraint.sense == LpConstraintGE:
        negative_row = [-element for element in row] # a'x >= constraint
        A_lt.append(negative_row)
        b_lt.append(constraint.constant)
        # b_lt.append(-constraint.constant)

# convert to numpy
A_lt = np.array(A_lt)
b_lt = np.array(b_lt)
A_eq = np.array(A_eq)
b_eq = np.array(b_eq)

# Extract the integer and binary constraints
binary_variables = []
integer_variables = []
continuous_variables = []
bounds = []
integrality_args = [0]*len(c) # Passes to the linprog if the variable is interger (1) or continuous (0)

for idx, var in enumerate(lp_problem.variables()):
    bounds.append((var.lowBound, var.upBound))

    if var.cat == 'Integer': # integer constraint
        # bounds.append((0.0, None))
        integer_variables.append(idx)
        # integrality_args[idx] = 1
    elif var.cat == 'Binary': # binary constraint
        # bounds.append((0.0, None))
        binary_variables.append(idx)
    else:
        # bounds.append((var.lowBound, var.upBound))
        # bounds.append((0.0, None))
        continuous_variables.append(idx) # continuous constraint

binary_variables = np.array(binary_variables)
integer_variables = np.array(integer_variables)
continuous_variables = np.array(continuous_variables)
integrality_args = integrality_args if sum(integrality_args) != 0 else 0

end = time.time()
print(f"Finished conversion, took {round(end-start, 5)} sec")

from scipy.optimize import linprog
print("Now starting to solve relaxed MILP...")

if A_lt.shape[0] != 0:
    start = time.time()
    # sol_init = linprog(c=c, A_ub=A_lt, b_ub=b_lt, A_eq=A_eq, b_eq=b_eq, integrality=integrality_args, bounds=bounds)
    sol_init, _ = BnB(c=c, A_ub=A_lt, b_ub=b_lt, A_eq=A_eq, b_eq=b_eq, bounds=bounds, int_indices=integer_variables)
    end = time.time()
else:
    start = time.time()
    # sol_init = linprog(c=c, A_eq=A_eq, b_eq=b_eq, integrality=integrality_args, bounds=bounds)
    # sol_init = linprog(c=c, A_eq=A_eq, b_eq=b_eq, bounds=bounds)
    sol_init, _ = BnB(c=c, A_eq=A_eq, b_eq=b_eq, bounds=bounds, int_indices=integer_variables)
    end = time.time()

print(f"Finished solving, took {round(end-start, 5)} sec")
print(f"solution: f(x*) = {sol_init.obj_val}")
# print(f"Pass: {sol_init.success}")
# print(f"Solution: {sol_init.x}")
# if not sol_init.success:
#     print(f"Solver message: {sol_init.message}")