From 6e326d5d6ef358e15eb7d366959189f52eed195c Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Mon, 5 Apr 2021 20:38:31 -0500 Subject: [PATCH] Move feature classes to features.py --- miplearn/components/component.py | 5 +- miplearn/components/lazy_static.py | 3 +- miplearn/components/objective.py | 3 +- miplearn/components/primal.py | 3 +- miplearn/features.py | 53 ++++++++++++++++--- miplearn/instance.py | 3 +- miplearn/solvers/learning.py | 4 +- miplearn/types.py | 47 +--------------- tests/components/steps/test_drop_redundant.py | 2 +- tests/components/test_lazy_static.py | 6 ++- tests/components/test_objective.py | 2 +- tests/components/test_primal.py | 2 +- tests/test_features.py | 8 ++- 13 files changed, 71 insertions(+), 70 deletions(-) diff --git a/miplearn/components/component.py b/miplearn/components/component.py index a182a44..e7451f0 100644 --- a/miplearn/components/component.py +++ b/miplearn/components/component.py @@ -7,7 +7,8 @@ from typing import Any, List, TYPE_CHECKING, Tuple, Dict, Hashable import numpy as np from miplearn.instance import Instance -from miplearn.types import LearningSolveStats, TrainingSample, Features +from miplearn.types import LearningSolveStats +from miplearn.features import TrainingSample, Features if TYPE_CHECKING: from miplearn.solvers.learning import LearningSolver @@ -49,7 +50,7 @@ class Component: their own statistics here. For example, PrimalSolutionComponent adds statistics regarding the number of predicted variables. All statistics in this dictionary are exported to the benchmark CSV file. - features: Features + features: miplearn.features.Features Features describing the model. training_data: TrainingSample A dictionary containing data that may be useful for training machine diff --git a/miplearn/components/lazy_static.py b/miplearn/components/lazy_static.py index e90041c..d67ba41 100644 --- a/miplearn/components/lazy_static.py +++ b/miplearn/components/lazy_static.py @@ -11,7 +11,8 @@ from miplearn import Classifier from miplearn.classifiers.counting import CountingClassifier from miplearn.classifiers.threshold import MinProbabilityThreshold, Threshold from miplearn.components.component import Component -from miplearn.types import TrainingSample, Features, LearningSolveStats +from miplearn.types import LearningSolveStats +from miplearn.features import TrainingSample, Features logger = logging.getLogger(__name__) diff --git a/miplearn/components/objective.py b/miplearn/components/objective.py index 0b13bfa..ddf59ec 100644 --- a/miplearn/components/objective.py +++ b/miplearn/components/objective.py @@ -12,7 +12,8 @@ from miplearn.classifiers import Regressor from miplearn.classifiers.sklearn import ScikitLearnRegressor from miplearn.components.component import Component from miplearn.instance import Instance -from miplearn.types import TrainingSample, LearningSolveStats, Features +from miplearn.types import LearningSolveStats +from miplearn.features import TrainingSample, Features if TYPE_CHECKING: from miplearn.solvers.learning import LearningSolver diff --git a/miplearn/components/primal.py b/miplearn/components/primal.py index a41377b..2229a31 100644 --- a/miplearn/components/primal.py +++ b/miplearn/components/primal.py @@ -22,11 +22,10 @@ from miplearn.components import classifier_evaluation_dict from miplearn.components.component import Component from miplearn.instance import Instance from miplearn.types import ( - TrainingSample, Solution, LearningSolveStats, - Features, ) +from miplearn.features import TrainingSample, Features logger = logging.getLogger(__name__) diff --git a/miplearn/features.py b/miplearn/features.py index 438ff2f..bc1f4d7 100644 --- a/miplearn/features.py +++ b/miplearn/features.py @@ -4,20 +4,57 @@ import collections import numbers -from typing import TYPE_CHECKING, Dict +from dataclasses import dataclass +from typing import TYPE_CHECKING, Dict, Optional, Set, List, Hashable -from miplearn.types import ( - Features, - ConstraintFeatures, - InstanceFeatures, - VariableFeatures, - VarIndex, -) +from miplearn.types import VarIndex, Solution if TYPE_CHECKING: from miplearn import InternalSolver, Instance +@dataclass +class TrainingSample: + lp_log: Optional[str] = None + lp_solution: Optional[Solution] = None + lp_value: Optional[float] = None + lazy_enforced: Optional[Set[str]] = None + lower_bound: Optional[float] = None + mip_log: Optional[str] = None + solution: Optional[Solution] = None + upper_bound: Optional[float] = None + slacks: Optional[Dict[str, float]] = None + + +@dataclass +class InstanceFeatures: + user_features: Optional[List[float]] = None + lazy_constraint_count: int = 0 + + +@dataclass +class VariableFeatures: + category: Optional[Hashable] = None + user_features: Optional[List[float]] = None + + +@dataclass +class ConstraintFeatures: + rhs: Optional[float] = None + lhs: Optional[Dict[str, float]] = None + sense: Optional[str] = None + category: Optional[Hashable] = None + user_features: Optional[List[float]] = None + lazy: bool = False + + +@dataclass +class Features: + instance: Optional[InstanceFeatures] = None + variables: Optional[Dict[str, Dict[VarIndex, VariableFeatures]]] = None + constraints: Optional[Dict[str, ConstraintFeatures]] = None + + class FeaturesExtractor: def __init__( self, diff --git a/miplearn/instance.py b/miplearn/instance.py index 93fff83..ed38d56 100644 --- a/miplearn/instance.py +++ b/miplearn/instance.py @@ -9,7 +9,8 @@ import pickle from abc import ABC, abstractmethod from typing import Any, List, Optional, Hashable, IO, cast -from miplearn.types import TrainingSample, VarIndex, Features +from miplearn.types import VarIndex +from miplearn.features import TrainingSample, Features logger = logging.getLogger(__name__) diff --git a/miplearn/solvers/learning.py b/miplearn/solvers/learning.py index d1ce423..b7c13ca 100644 --- a/miplearn/solvers/learning.py +++ b/miplearn/solvers/learning.py @@ -13,12 +13,12 @@ from miplearn.components.cuts import UserCutsComponent from miplearn.components.lazy_dynamic import DynamicLazyConstraintsComponent from miplearn.components.objective import ObjectiveValueComponent from miplearn.components.primal import PrimalSolutionComponent -from miplearn.features import FeaturesExtractor +from miplearn.features import FeaturesExtractor, TrainingSample from miplearn.instance import Instance, PickleGzInstance from miplearn.solvers import _RedirectOutput from miplearn.solvers.internal import InternalSolver from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver -from miplearn.types import TrainingSample, LearningSolveStats +from miplearn.types import LearningSolveStats logger = logging.getLogger(__name__) diff --git a/miplearn/types.py b/miplearn/types.py index 802905b..6ad7dcf 100644 --- a/miplearn/types.py +++ b/miplearn/types.py @@ -2,8 +2,7 @@ # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -from typing import Optional, Dict, Callable, Any, Union, Tuple, List, Set, Hashable -from dataclasses import dataclass +from typing import Optional, Dict, Callable, Any, Union, Tuple from mypy_extensions import TypedDict @@ -11,20 +10,6 @@ VarIndex = Union[str, int, Tuple[Union[str, int]]] Solution = Dict[str, Dict[VarIndex, Optional[float]]] - -@dataclass -class TrainingSample: - lp_log: Optional[str] = None - lp_solution: Optional[Solution] = None - lp_value: Optional[float] = None - lazy_enforced: Optional[Set[str]] = None - lower_bound: Optional[float] = None - mip_log: Optional[str] = None - solution: Optional[Solution] = None - upper_bound: Optional[float] = None - slacks: Optional[Dict[str, float]] = None - - LPSolveStats = TypedDict( "LPSolveStats", { @@ -75,36 +60,6 @@ LearningSolveStats = TypedDict( total=False, ) - -@dataclass -class InstanceFeatures: - user_features: Optional[List[float]] = None - lazy_constraint_count: int = 0 - - -@dataclass -class VariableFeatures: - category: Optional[Hashable] = None - user_features: Optional[List[float]] = None - - -@dataclass -class ConstraintFeatures: - rhs: Optional[float] = None - lhs: Optional[Dict[str, float]] = None - sense: Optional[str] = None - category: Optional[Hashable] = None - user_features: Optional[List[float]] = None - lazy: bool = False - - -@dataclass -class Features: - instance: Optional[InstanceFeatures] = None - variables: Optional[Dict[str, Dict[VarIndex, VariableFeatures]]] = None - constraints: Optional[Dict[str, ConstraintFeatures]] = None - - IterationCallback = Callable[[], bool] LazyCallback = Callable[[Any, Any], None] diff --git a/tests/components/steps/test_drop_redundant.py b/tests/components/steps/test_drop_redundant.py index d032721..2efe548 100644 --- a/tests/components/steps/test_drop_redundant.py +++ b/tests/components/steps/test_drop_redundant.py @@ -12,7 +12,7 @@ from miplearn.components.steps.drop_redundant import DropRedundantInequalitiesSt from miplearn.instance import Instance from miplearn.solvers.internal import InternalSolver from miplearn.solvers.learning import LearningSolver -from miplearn.types import TrainingSample, Features +from miplearn.features import TrainingSample, Features from tests.fixtures.infeasible import get_infeasible_instance from tests.fixtures.redundant import get_instance_with_redundancy diff --git a/tests/components/test_lazy_static.py b/tests/components/test_lazy_static.py index 4337062..a26c5c5 100644 --- a/tests/components/test_lazy_static.py +++ b/tests/components/test_lazy_static.py @@ -13,11 +13,13 @@ from miplearn.classifiers import Classifier from miplearn.classifiers.threshold import Threshold, MinProbabilityThreshold from miplearn.components.lazy_static import StaticLazyConstraintsComponent from miplearn.types import ( - TrainingSample, - Features, LearningSolveStats, +) +from miplearn.features import ( + TrainingSample, InstanceFeatures, ConstraintFeatures, + Features, ) diff --git a/tests/components/test_objective.py b/tests/components/test_objective.py index da9ec06..a3b8a4f 100644 --- a/tests/components/test_objective.py +++ b/tests/components/test_objective.py @@ -9,7 +9,7 @@ from numpy.testing import assert_array_equal from miplearn import GurobiPyomoSolver, LearningSolver, Regressor from miplearn.components.objective import ObjectiveValueComponent -from miplearn.types import TrainingSample, Features, InstanceFeatures +from miplearn.features import TrainingSample, InstanceFeatures, Features from tests.fixtures.knapsack import get_knapsack_instance import numpy as np diff --git a/tests/components/test_primal.py b/tests/components/test_primal.py index de92749..17d7c25 100644 --- a/tests/components/test_primal.py +++ b/tests/components/test_primal.py @@ -13,7 +13,7 @@ from miplearn.classifiers.threshold import Threshold from miplearn.components import classifier_evaluation_dict from miplearn.components.primal import PrimalSolutionComponent from miplearn.problems.tsp import TravelingSalesmanGenerator -from miplearn.types import TrainingSample, Features, VariableFeatures +from miplearn.features import TrainingSample, VariableFeatures, Features def test_xy() -> None: diff --git a/tests/test_features.py b/tests/test_features.py index 60b0b9b..b4e964f 100644 --- a/tests/test_features.py +++ b/tests/test_features.py @@ -3,8 +3,12 @@ # Released under the modified BSD license. See COPYING.md for more details. from miplearn import GurobiSolver -from miplearn.features import FeaturesExtractor -from miplearn.types import VariableFeatures, InstanceFeatures, ConstraintFeatures +from miplearn.features import ( + FeaturesExtractor, + InstanceFeatures, + VariableFeatures, + ConstraintFeatures, +) from tests.fixtures.knapsack import get_knapsack_instance