def _extract_bound_violations(
    grb_model, variables, negative_slack_prefix, positive_slack_prefix
):
    """
    Extracts the lower and upper bound changes that have to applied to
    the input model in order to make its solution feasible.
    """
    # Extracts the magnitude of the bound violations on the relaxed
    # Gurobi model
    epsilon = grb_model.Params.FeasibilityTol
    violations = []
    for var in variables:
        lb_change = -grb_model.getVarByName(negative_slack_prefix + var).X
        ub_change = grb_model.getVarByName(positive_slack_prefix + var).X
        if lb_change < -epsilon:
            violations.append((var, lb_change - epsilon, 0))
        elif ub_change > epsilon:
            violations.append((var, 0, ub_change + epsilon))
    return violations