Implement component.fit, component.fit_xy

master
Alinson S. Xavier 5 years ago
parent 205a972937
commit 1224613b1a

@ -2,9 +2,10 @@
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
from abc import ABC, abstractmethod import numpy as np
from typing import Any, List, Union, TYPE_CHECKING, Tuple, Dict from typing import Any, List, Union, TYPE_CHECKING, Tuple, Dict
from miplearn.extractors import InstanceIterator
from miplearn.instance import Instance from miplearn.instance import Instance
from miplearn.types import LearningSolveStats, TrainingSample from miplearn.types import LearningSolveStats, TrainingSample
@ -13,7 +14,7 @@ if TYPE_CHECKING:
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class Component(ABC): class Component:
""" """
A Component is an object which adds functionality to a LearningSolver. A Component is an object which adds functionality to a LearningSolver.
@ -130,12 +131,6 @@ class Component(ABC):
""" """
return return
def fit(
self,
training_instances: Union[List[str], List[Instance]],
) -> None:
return
@staticmethod @staticmethod
def xy_sample( def xy_sample(
instance: Any, instance: Any,
@ -147,6 +142,40 @@ class Component(ABC):
""" """
return {}, {} return {}, {}
def xy_instances(
self,
instances: Union[List[str], List[Instance]],
) -> Tuple[Dict, Dict]:
x_combined: Dict = {}
y_combined: Dict = {}
for instance in InstanceIterator(instances):
for sample in instance.training_data:
x_sample, y_sample = self.xy_sample(instance, sample)
for cat in x_sample.keys():
if cat not in x_combined:
x_combined[cat] = []
y_combined[cat] = []
x_combined[cat] += x_sample[cat]
y_combined[cat] += y_sample[cat]
return x_combined, y_combined
def fit(
self,
training_instances: Union[List[str], List[Instance]],
) -> None:
x, y = self.xy_instances(training_instances)
for cat in x.keys():
x[cat] = np.array(x[cat])
y[cat] = np.array(y[cat])
self.fit_xy(x, y)
def fit_xy(
self,
x: Dict[str, np.ndarray],
y: Dict[str, np.ndarray],
) -> None:
return
def iteration_cb( def iteration_cb(
self, self,
solver: "LearningSolver", solver: "LearningSolver",

@ -105,19 +105,11 @@ class PrimalSolutionComponent(Component):
) -> Dict[Hashable, np.ndarray]: ) -> Dict[Hashable, np.ndarray]:
return self._build_x_y_dict(instances, self._extract_variable_features) return self._build_x_y_dict(instances, self._extract_variable_features)
def y( def fit_xy(
self, self,
instances: Union[List[str], List[Instance]], x: Dict[str, np.ndarray],
) -> Dict[Hashable, np.ndarray]: y: Dict[str, np.ndarray],
return self._build_x_y_dict(instances, self._extract_variable_labels)
def fit(
self,
training_instances: Union[List[str], List[Instance]],
n_jobs: int = 1,
) -> None: ) -> None:
x = self.x(training_instances)
y = self.y(training_instances)
for category in x.keys(): for category in x.keys():
clf = self.classifier_factory() clf = self.classifier_factory()
thr = self.threshold_factory() thr = self.threshold_factory()
@ -322,7 +314,10 @@ class PrimalSolutionComponent(Component):
x[category] = [] x[category] = []
y[category] = [] y[category] = []
features: Any = instance.get_variable_features(var, idx) features: Any = instance.get_variable_features(var, idx)
assert isinstance(features, list)
if "LP solution" in sample and sample["LP solution"] is not None: if "LP solution" in sample and sample["LP solution"] is not None:
lp_value = sample["LP solution"][var][idx]
if lp_value is not None:
features += [sample["LP solution"][var][idx]] features += [sample["LP solution"][var][idx]]
x[category] += [features] x[category] += [features]
y[category] += [[opt_value < 0.5, opt_value >= 0.5]] y[category] += [[opt_value < 0.5, opt_value >= 0.5]]

@ -265,20 +265,16 @@ class KnapsackInstance(Instance):
return model return model
def get_instance_features(self): def get_instance_features(self):
return np.array( return [
[
self.capacity, self.capacity,
np.average(self.weights), np.average(self.weights),
] ]
)
def get_variable_features(self, var, index): def get_variable_features(self, var, index):
return np.array( return [
[
self.weights[index], self.weights[index],
self.prices[index], self.prices[index],
] ]
)
class GurobiKnapsackInstance(KnapsackInstance): class GurobiKnapsackInstance(KnapsackInstance):

@ -129,7 +129,7 @@ class MaxWeightStableSetInstance(Instance):
features += neighbor_weights[:5] features += neighbor_weights[:5]
features += neighbor_degrees[:5] features += neighbor_degrees[:5]
features += [self.graph.degree(index)] features += [self.graph.degree(index)]
return np.array(features) return features
def get_variable_category(self, var, index): def get_variable_category(self, var, index):
return "default" return "default"

@ -157,10 +157,10 @@ class TravelingSalesmanInstance(Instance):
return model return model
def get_instance_features(self): def get_instance_features(self):
return np.array([1]) return [1]
def get_variable_features(self, var_name, index): def get_variable_features(self, var_name, index):
return np.array([1]) return [1]
def get_variable_category(self, var_name, index): def get_variable_category(self, var_name, index):
return index return index

@ -0,0 +1,97 @@
# 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.
from unittest.mock import Mock
from miplearn import Component, Instance
def test_xy_instance():
def _xy_sample(instance, sample):
print(sample)
x = {
"s1": {
"category_a": [
[1, 2, 3],
[3, 4, 6],
],
"category_b": [
[7, 8, 9],
],
},
"s2": {
"category_a": [
[0, 0, 0],
[0, 5, 3],
[2, 2, 0],
],
"category_c": [
[0, 0, 0],
[0, 0, 1],
],
},
"s3": {
"category_c": [
[1, 1, 1],
],
},
}
y = {
"s1": {
"category_a": [[1], [2]],
"category_b": [[3]],
},
"s2": {
"category_a": [[4], [5], [6]],
"category_c": [[8], [9], [10]],
},
"s3": {
"category_c": [[11]],
},
}
return x[sample], y[sample]
comp = Component()
instance_1 = Mock(spec=Instance)
instance_1.training_data = ["s1", "s2"]
instance_2 = Mock(spec=Instance)
instance_2.training_data = ["s3"]
comp.xy_sample = _xy_sample
x_expected = {
"category_a": [
[1, 2, 3],
[3, 4, 6],
[0, 0, 0],
[0, 5, 3],
[2, 2, 0],
],
"category_b": [
[7, 8, 9],
],
"category_c": [
[0, 0, 0],
[0, 0, 1],
[1, 1, 1],
],
}
y_expected = {
"category_a": [
[1],
[2],
[4],
[5],
[6],
],
"category_b": [
[3],
],
"category_c": [
[8],
[9],
[10],
[11],
],
}
x_actual, y_actual = comp.xy_instances([instance_1, instance_2])
assert x_actual == x_expected
assert y_actual == y_expected

@ -130,177 +130,6 @@ def test_xy_sample_without_lp_solution() -> None:
assert_array_equal(y_actual["default"], y_expected["default"]) assert_array_equal(y_actual["default"], y_expected["default"])
def test_x_y_fit() -> None:
comp = PrimalSolutionComponent()
training_instances = cast(
List[Instance],
[
Mock(spec=Instance),
Mock(spec=Instance),
],
)
# Construct first instance
training_instances[0].get_variable_category = Mock( # type: ignore
side_effect=lambda var_name, index: {
0: "default",
1: None,
2: "default",
3: "default",
}[index]
)
training_instances[0].get_variable_features = Mock( # type: ignore
side_effect=lambda var, index: {
0: [0.0, 0.0],
1: [0.0, 1.0],
2: [1.0, 0.0],
3: [1.0, 1.0],
}[index]
)
training_instances[0].training_data = [
{
"Solution": {
"x": {
0: 0.0,
1: 1.0,
2: 0.0,
3: 0.0,
}
},
"LP solution": {
"x": {
0: 0.1,
1: 0.1,
2: 0.1,
3: 0.1,
}
},
},
{
"Solution": {
"x": {
0: 0.0,
1: 1.0,
2: 1.0,
3: 0.0,
}
},
"LP solution": {
"x": {
0: 0.2,
1: 0.2,
2: 0.2,
3: 0.2,
}
},
},
]
# Construct second instance
training_instances[1].get_variable_category = Mock( # type: ignore
side_effect=lambda var_name, index: {
0: "default",
1: None,
2: "default",
3: "default",
}[index]
)
training_instances[1].get_variable_features = Mock( # type: ignore
side_effect=lambda var, index: {
0: [0.0, 0.0],
1: [0.0, 2.0],
2: [2.0, 0.0],
3: [2.0, 2.0],
}[index]
)
training_instances[1].training_data = [
{
"Solution": {
"x": {
0: 1.0,
1: 1.0,
2: 1.0,
3: 1.0,
}
},
"LP solution": {
"x": {
0: 0.3,
1: 0.3,
2: 0.3,
3: 0.3,
}
},
},
{
"Solution": None,
"LP solution": None,
},
]
# Test x
x_expected = {
"default": np.array(
[
[0.0, 0.0, 0.1],
[1.0, 0.0, 0.1],
[1.0, 1.0, 0.1],
[0.0, 0.0, 0.2],
[1.0, 0.0, 0.2],
[1.0, 1.0, 0.2],
[0.0, 0.0, 0.3],
[2.0, 0.0, 0.3],
[2.0, 2.0, 0.3],
]
)
}
x_actual = comp.x(training_instances)
assert len(x_actual.keys()) == 1
assert_array_equal(x_actual["default"], x_expected["default"])
# Test y
y_expected = {
"default": np.array(
[
[True, False],
[True, False],
[True, False],
[True, False],
[False, True],
[True, False],
[False, True],
[False, True],
[False, True],
]
)
}
y_actual = comp.y(training_instances)
assert len(y_actual.keys()) == 1
assert_array_equal(y_actual["default"], y_expected["default"])
# Test fit
classifier = Mock(spec=Classifier)
threshold = Mock(spec=Threshold)
classifier_factory = Mock(return_value=classifier)
threshold_factory = Mock(return_value=threshold)
comp = PrimalSolutionComponent(
classifier=classifier_factory,
threshold=threshold_factory,
)
comp.fit(training_instances)
# Should build and train classifier for "default" category
classifier_factory.assert_called_once()
assert_array_equal(x_actual["default"], classifier.fit.call_args[0][0])
assert_array_equal(y_actual["default"], classifier.fit.call_args[0][1])
# Should build and train threshold for "default" category
threshold_factory.assert_called_once()
assert classifier == threshold.fit.call_args[0][0]
assert_array_equal(x_actual["default"], threshold.fit.call_args[0][1])
assert_array_equal(y_actual["default"], threshold.fit.call_args[0][2])
def test_predict() -> None: def test_predict() -> None:
comp = PrimalSolutionComponent() comp = PrimalSolutionComponent()

Loading…
Cancel
Save