Module miplearn.components.objective

Expand source code
#  MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
#  Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
#  Released under the modified BSD license. See COPYING.md for more details.

import logging
from copy import deepcopy

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import (
    mean_squared_error,
    explained_variance_score,
    max_error,
    mean_absolute_error,
    r2_score,
)

from miplearn.classifiers import Regressor
from miplearn.components.component import Component
from miplearn.extractors import InstanceFeaturesExtractor, ObjectiveValueExtractor

logger = logging.getLogger(__name__)


class ObjectiveValueComponent(Component):
    """
    A Component which predicts the optimal objective value of the problem.
    """

    def __init__(
        self,
        regressor: Regressor = LinearRegression(),
    ) -> None:
        self.ub_regressor = None
        self.lb_regressor = None
        self.regressor_prototype = regressor

    def before_solve(self, solver, instance, model):
        if self.ub_regressor is not None:
            logger.info("Predicting optimal value...")
            lb, ub = self.predict([instance])[0]
            instance.predicted_ub = ub
            instance.predicted_lb = lb
            logger.info("Predicted values: lb=%.2f, ub=%.2f" % (lb, ub))

    def after_solve(
        self,
        solver,
        instance,
        model,
        stats,
        training_data,
    ):
        if self.ub_regressor is not None:
            stats["Predicted UB"] = instance.predicted_ub
            stats["Predicted LB"] = instance.predicted_lb
        else:
            stats["Predicted UB"] = None
            stats["Predicted LB"] = None

    def fit(self, training_instances):
        logger.debug("Extracting features...")
        features = InstanceFeaturesExtractor().extract(training_instances)
        ub = ObjectiveValueExtractor(kind="upper bound").extract(training_instances)
        lb = ObjectiveValueExtractor(kind="lower bound").extract(training_instances)
        assert ub.shape == (len(training_instances), 1)
        assert lb.shape == (len(training_instances), 1)
        self.ub_regressor = deepcopy(self.regressor_prototype)
        self.lb_regressor = deepcopy(self.regressor_prototype)
        logger.debug("Fitting ub_regressor...")
        self.ub_regressor.fit(features, ub.ravel())
        logger.debug("Fitting ub_regressor...")
        self.lb_regressor.fit(features, lb.ravel())

    def predict(self, instances):
        features = InstanceFeaturesExtractor().extract(instances)
        lb = self.lb_regressor.predict(features)
        ub = self.ub_regressor.predict(features)
        assert lb.shape == (len(instances),)
        assert ub.shape == (len(instances),)
        return np.array([lb, ub]).T

    def evaluate(self, instances):
        y_pred = self.predict(instances)
        y_true = np.array(
            [
                [
                    inst.training_data[0]["Lower bound"],
                    inst.training_data[0]["Upper bound"],
                ]
                for inst in instances
            ]
        )
        y_true_lb, y_true_ub = y_true[:, 0], y_true[:, 1]
        y_pred_lb, y_pred_ub = y_pred[:, 1], y_pred[:, 1]
        ev = {
            "Lower bound": {
                "Mean squared error": mean_squared_error(y_true_lb, y_pred_lb),
                "Explained variance": explained_variance_score(y_true_lb, y_pred_lb),
                "Max error": max_error(y_true_lb, y_pred_lb),
                "Mean absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
                "R2": r2_score(y_true_lb, y_pred_lb),
                "Median absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
            },
            "Upper bound": {
                "Mean squared error": mean_squared_error(y_true_ub, y_pred_ub),
                "Explained variance": explained_variance_score(y_true_ub, y_pred_ub),
                "Max error": max_error(y_true_ub, y_pred_ub),
                "Mean absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
                "R2": r2_score(y_true_ub, y_pred_ub),
                "Median absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
            },
        }
        return ev

Classes

class ObjectiveValueComponent (regressor=LinearRegression())

A Component which predicts the optimal objective value of the problem.

Expand source code
class ObjectiveValueComponent(Component):
    """
    A Component which predicts the optimal objective value of the problem.
    """

    def __init__(
        self,
        regressor: Regressor = LinearRegression(),
    ) -> None:
        self.ub_regressor = None
        self.lb_regressor = None
        self.regressor_prototype = regressor

    def before_solve(self, solver, instance, model):
        if self.ub_regressor is not None:
            logger.info("Predicting optimal value...")
            lb, ub = self.predict([instance])[0]
            instance.predicted_ub = ub
            instance.predicted_lb = lb
            logger.info("Predicted values: lb=%.2f, ub=%.2f" % (lb, ub))

    def after_solve(
        self,
        solver,
        instance,
        model,
        stats,
        training_data,
    ):
        if self.ub_regressor is not None:
            stats["Predicted UB"] = instance.predicted_ub
            stats["Predicted LB"] = instance.predicted_lb
        else:
            stats["Predicted UB"] = None
            stats["Predicted LB"] = None

    def fit(self, training_instances):
        logger.debug("Extracting features...")
        features = InstanceFeaturesExtractor().extract(training_instances)
        ub = ObjectiveValueExtractor(kind="upper bound").extract(training_instances)
        lb = ObjectiveValueExtractor(kind="lower bound").extract(training_instances)
        assert ub.shape == (len(training_instances), 1)
        assert lb.shape == (len(training_instances), 1)
        self.ub_regressor = deepcopy(self.regressor_prototype)
        self.lb_regressor = deepcopy(self.regressor_prototype)
        logger.debug("Fitting ub_regressor...")
        self.ub_regressor.fit(features, ub.ravel())
        logger.debug("Fitting ub_regressor...")
        self.lb_regressor.fit(features, lb.ravel())

    def predict(self, instances):
        features = InstanceFeaturesExtractor().extract(instances)
        lb = self.lb_regressor.predict(features)
        ub = self.ub_regressor.predict(features)
        assert lb.shape == (len(instances),)
        assert ub.shape == (len(instances),)
        return np.array([lb, ub]).T

    def evaluate(self, instances):
        y_pred = self.predict(instances)
        y_true = np.array(
            [
                [
                    inst.training_data[0]["Lower bound"],
                    inst.training_data[0]["Upper bound"],
                ]
                for inst in instances
            ]
        )
        y_true_lb, y_true_ub = y_true[:, 0], y_true[:, 1]
        y_pred_lb, y_pred_ub = y_pred[:, 1], y_pred[:, 1]
        ev = {
            "Lower bound": {
                "Mean squared error": mean_squared_error(y_true_lb, y_pred_lb),
                "Explained variance": explained_variance_score(y_true_lb, y_pred_lb),
                "Max error": max_error(y_true_lb, y_pred_lb),
                "Mean absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
                "R2": r2_score(y_true_lb, y_pred_lb),
                "Median absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
            },
            "Upper bound": {
                "Mean squared error": mean_squared_error(y_true_ub, y_pred_ub),
                "Explained variance": explained_variance_score(y_true_ub, y_pred_ub),
                "Max error": max_error(y_true_ub, y_pred_ub),
                "Mean absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
                "R2": r2_score(y_true_ub, y_pred_ub),
                "Median absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
            },
        }
        return ev

Ancestors

Methods

def evaluate(self, instances)
Expand source code
def evaluate(self, instances):
    y_pred = self.predict(instances)
    y_true = np.array(
        [
            [
                inst.training_data[0]["Lower bound"],
                inst.training_data[0]["Upper bound"],
            ]
            for inst in instances
        ]
    )
    y_true_lb, y_true_ub = y_true[:, 0], y_true[:, 1]
    y_pred_lb, y_pred_ub = y_pred[:, 1], y_pred[:, 1]
    ev = {
        "Lower bound": {
            "Mean squared error": mean_squared_error(y_true_lb, y_pred_lb),
            "Explained variance": explained_variance_score(y_true_lb, y_pred_lb),
            "Max error": max_error(y_true_lb, y_pred_lb),
            "Mean absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
            "R2": r2_score(y_true_lb, y_pred_lb),
            "Median absolute error": mean_absolute_error(y_true_lb, y_pred_lb),
        },
        "Upper bound": {
            "Mean squared error": mean_squared_error(y_true_ub, y_pred_ub),
            "Explained variance": explained_variance_score(y_true_ub, y_pred_ub),
            "Max error": max_error(y_true_ub, y_pred_ub),
            "Mean absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
            "R2": r2_score(y_true_ub, y_pred_ub),
            "Median absolute error": mean_absolute_error(y_true_ub, y_pred_ub),
        },
    }
    return ev
def fit(self, training_instances)
Expand source code
def fit(self, training_instances):
    logger.debug("Extracting features...")
    features = InstanceFeaturesExtractor().extract(training_instances)
    ub = ObjectiveValueExtractor(kind="upper bound").extract(training_instances)
    lb = ObjectiveValueExtractor(kind="lower bound").extract(training_instances)
    assert ub.shape == (len(training_instances), 1)
    assert lb.shape == (len(training_instances), 1)
    self.ub_regressor = deepcopy(self.regressor_prototype)
    self.lb_regressor = deepcopy(self.regressor_prototype)
    logger.debug("Fitting ub_regressor...")
    self.ub_regressor.fit(features, ub.ravel())
    logger.debug("Fitting ub_regressor...")
    self.lb_regressor.fit(features, lb.ravel())
def predict(self, instances)
Expand source code
def predict(self, instances):
    features = InstanceFeaturesExtractor().extract(instances)
    lb = self.lb_regressor.predict(features)
    ub = self.ub_regressor.predict(features)
    assert lb.shape == (len(instances),)
    assert ub.shape == (len(instances),)
    return np.array([lb, ub]).T

Inherited members