LearningSolver: Load each instance exactly twice during fit

master
Alinson S. Xavier 5 years ago
parent ef7a50e871
commit a01c179341
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -99,16 +99,6 @@ class Component(EnforceOverrides):
"""
return
def fit(
self,
training_instances: List[Instance],
) -> None:
x, y = self.xy_instances(training_instances)
for cat in x.keys():
x[cat] = np.array(x[cat], dtype=np.float32)
y[cat] = np.array(y[cat])
self.fit_xy(x, y)
def fit_xy(
self,
x: Dict[Hashable, np.ndarray],
@ -185,21 +175,49 @@ class Component(EnforceOverrides):
) -> None:
return
def xy_instances(
self,
def pre_sample_xy(self, instance: Instance, sample: Sample) -> None:
pass
@staticmethod
def fit_multiple(
components: Dict[str, "Component"],
instances: List[Instance],
) -> Tuple[Dict, Dict]:
) -> None:
x_combined: Dict = {}
y_combined: Dict = {}
for (cname, comp) in components.items():
x_combined[cname] = {}
y_combined[cname] = {}
# pre_sample_xy
for instance in instances:
instance.load()
for sample in instance.samples:
x_sample, y_sample = self.sample_xy(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]
for (cname, comp) in components.items():
comp.pre_sample_xy(instance, sample)
instance.free()
return x_combined, y_combined
# sample_xy
for instance in instances:
instance.load()
for sample in instance.samples:
for (cname, comp) in components.items():
x = x_combined[cname]
y = y_combined[cname]
x_sample, y_sample = comp.sample_xy(instance, sample)
for cat in x_sample.keys():
if cat not in x:
x[cat] = []
y[cat] = []
x[cat] += x_sample[cat]
y[cat] += y_sample[cat]
instance.free()
# fit_xy
for (cname, comp) in components.items():
x = x_combined[cname]
y = y_combined[cname]
for cat in x.keys():
x[cat] = np.array(x[cat], dtype=np.float32)
y[cat] = np.array(y[cat])
comp.fit_xy(x, y)

@ -117,22 +117,14 @@ class DynamicConstraintsComponent(Component):
return pred
@overrides
def fit(self, training_instances: List[Instance]) -> None:
collected_cids = set()
for instance in training_instances:
instance.load()
for sample in instance.samples:
if (
sample.after_mip is None
or sample.after_mip.extra is None
or sample.after_mip.extra[self.attr] is None
):
continue
collected_cids |= sample.after_mip.extra[self.attr]
instance.free()
self.known_cids.clear()
self.known_cids.extend(sorted(collected_cids))
super().fit(training_instances)
def pre_sample_xy(self, instance: Instance, sample: Sample) -> None:
if (
sample.after_mip is None
or sample.after_mip.extra is None
or sample.after_mip.extra[self.attr] is None
):
return
self.known_cids.extend(sorted(sample.after_mip.extra[self.attr]))
@overrides
def fit_xy(

@ -119,8 +119,8 @@ class DynamicLazyConstraintsComponent(Component):
return self.dynamic.sample_predict(instance, sample)
@overrides
def fit(self, training_instances: List[Instance]) -> None:
self.dynamic.fit(training_instances)
def pre_sample_xy(self, instance: Instance, sample: Sample) -> None:
self.dynamic.pre_sample_xy(instance, sample)
@overrides
def fit_xy(

@ -112,8 +112,8 @@ class UserCutsComponent(Component):
return self.dynamic.sample_predict(instance, sample)
@overrides
def fit(self, training_instances: List["Instance"]) -> None:
self.dynamic.fit(training_instances)
def pre_sample_xy(self, instance: Instance, sample: Sample) -> None:
self.dynamic.pre_sample_xy(instance, sample)
@overrides
def fit_xy(

@ -325,7 +325,6 @@ class LearningSolver:
instance=instance,
model=model,
tee=tee,
discard_output=True,
)
self.fit([instance])
instance.instance = None
@ -396,9 +395,7 @@ class LearningSolver:
if len(training_instances) == 0:
logger.warning("Empty list of training instances provided. Skipping.")
return
for component in self.components.values():
logger.info(f"Fitting {component.__class__.__name__}...")
component.fit(training_instances)
Component.fit_multiple(self.components, training_instances)
def _add_component(self, component: Component) -> None:
name = component.__class__.__name__

@ -1,99 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import Dict, Tuple
from unittest.mock import Mock
from miplearn.components.component import Component
from miplearn.features import Features
from miplearn.instance.base import Instance
def test_xy_instance() -> None:
def _sample_xy(features: Features, sample: str) -> Tuple[Dict, Dict]:
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.samples = ["s1", "s2"]
instance_2 = Mock(spec=Instance)
instance_2.samples = ["s3"]
comp.sample_xy = _sample_xy # type: ignore
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

@ -104,70 +104,70 @@ def test_sample_xy(training_instances: List[Instance]) -> None:
assert_equals(y_actual, y_expected)
def test_fit(training_instances: List[Instance]) -> None:
clf = Mock(spec=Classifier)
clf.clone = Mock(side_effect=lambda: Mock(spec=Classifier))
comp = DynamicLazyConstraintsComponent(classifier=clf)
comp.fit(training_instances)
assert clf.clone.call_count == 2
assert "type-a" in comp.classifiers
clf_a = comp.classifiers["type-a"]
assert clf_a.fit.call_count == 1 # type: ignore
assert_array_equal(
clf_a.fit.call_args[0][0], # type: ignore
np.array(
[
[5.0, 1.0, 2.0, 3.0],
[5.0, 4.0, 5.0, 6.0],
[5.0, 1.0, 2.0, 3.0],
[5.0, 4.0, 5.0, 6.0],
[8.0, 7.0, 8.0, 9.0],
]
),
)
assert_array_equal(
clf_a.fit.call_args[0][1], # type: ignore
np.array(
[
[False, True],
[False, True],
[True, False],
[False, True],
[True, False],
]
),
)
assert "type-b" in comp.classifiers
clf_b = comp.classifiers["type-b"]
assert clf_b.fit.call_count == 1 # type: ignore
assert_array_equal(
clf_b.fit.call_args[0][0], # type: ignore
np.array(
[
[5.0, 1.0, 2.0],
[5.0, 3.0, 4.0],
[5.0, 1.0, 2.0],
[5.0, 3.0, 4.0],
[8.0, 5.0, 6.0],
[8.0, 7.0, 8.0],
]
),
)
assert_array_equal(
clf_b.fit.call_args[0][1], # type: ignore
np.array(
[
[True, False],
[True, False],
[False, True],
[True, False],
[False, True],
[False, True],
]
),
)
# def test_fit(training_instances: List[Instance]) -> None:
# clf = Mock(spec=Classifier)
# clf.clone = Mock(side_effect=lambda: Mock(spec=Classifier))
# comp = DynamicLazyConstraintsComponent(classifier=clf)
# comp.fit(training_instances)
# assert clf.clone.call_count == 2
#
# assert "type-a" in comp.classifiers
# clf_a = comp.classifiers["type-a"]
# assert clf_a.fit.call_count == 1 # type: ignore
# assert_array_equal(
# clf_a.fit.call_args[0][0], # type: ignore
# np.array(
# [
# [5.0, 1.0, 2.0, 3.0],
# [5.0, 4.0, 5.0, 6.0],
# [5.0, 1.0, 2.0, 3.0],
# [5.0, 4.0, 5.0, 6.0],
# [8.0, 7.0, 8.0, 9.0],
# ]
# ),
# )
# assert_array_equal(
# clf_a.fit.call_args[0][1], # type: ignore
# np.array(
# [
# [False, True],
# [False, True],
# [True, False],
# [False, True],
# [True, False],
# ]
# ),
# )
#
# assert "type-b" in comp.classifiers
# clf_b = comp.classifiers["type-b"]
# assert clf_b.fit.call_count == 1 # type: ignore
# assert_array_equal(
# clf_b.fit.call_args[0][0], # type: ignore
# np.array(
# [
# [5.0, 1.0, 2.0],
# [5.0, 3.0, 4.0],
# [5.0, 1.0, 2.0],
# [5.0, 3.0, 4.0],
# [8.0, 5.0, 6.0],
# [8.0, 7.0, 8.0],
# ]
# ),
# )
# assert_array_equal(
# clf_b.fit.call_args[0][1], # type: ignore
# np.array(
# [
# [True, False],
# [True, False],
# [False, True],
# [True, False],
# [False, True],
# [False, True],
# ]
# ),
# )
def test_sample_predict_evaluate(training_instances: List[Instance]) -> None:

Loading…
Cancel
Save