diff --git a/.gitignore b/.gitignore index dcc781c..d3aef9c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,6 @@ eggs/ env.bak/ env/ htmlcov/ -instance/ ipython_config.py lib/ lib64/ diff --git a/miplearn/__init__.py b/miplearn/__init__.py index 41ffdfe..3ed8991 100644 --- a/miplearn/__init__.py +++ b/miplearn/__init__.py @@ -3,15 +3,9 @@ # Released under the modified BSD license. See COPYING.md for more details. from .benchmark import BenchmarkRunner -from .classifiers import ( - Classifier, - Regressor, -) +from .classifiers import Classifier, Regressor from .classifiers.adaptive import AdaptiveClassifier -from .classifiers.sklearn import ( - ScikitLearnRegressor, - ScikitLearnClassifier, -) +from .classifiers.sklearn import ScikitLearnRegressor, ScikitLearnClassifier from .classifiers.threshold import MinPrecisionThreshold from .components.component import Component from .components.dynamic_lazy import DynamicLazyConstraintsComponent @@ -22,12 +16,12 @@ from .components.static_lazy import StaticLazyConstraintsComponent from .components.steps.convert_tight import ConvertTightIneqsIntoEqsStep from .components.steps.drop_redundant import DropRedundantInequalitiesStep from .components.steps.relax_integrality import RelaxIntegralityStep -from .instance import ( - Instance, +from .instance.base import Instance +from .instance.picklegz import ( PickleGzInstance, write_pickle_gz, - write_pickle_gz_multiple, read_pickle_gz, + write_pickle_gz_multiple, ) from .log import setup_logger from .solvers.gurobi import GurobiSolver diff --git a/miplearn/benchmark.py b/miplearn/benchmark.py index ccb9426..559ed5d 100644 --- a/miplearn/benchmark.py +++ b/miplearn/benchmark.py @@ -8,7 +8,7 @@ from typing import Dict, List import pandas as pd -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.learning import LearningSolver logger = logging.getLogger(__name__) diff --git a/miplearn/components/component.py b/miplearn/components/component.py index a2eec63..e5aa799 100644 --- a/miplearn/components/component.py +++ b/miplearn/components/component.py @@ -7,7 +7,7 @@ from typing import Any, List, TYPE_CHECKING, Tuple, Dict, Hashable import numpy as np from miplearn.features import TrainingSample, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.types import LearningSolveStats if TYPE_CHECKING: diff --git a/miplearn/components/objective.py b/miplearn/components/objective.py index 06be3da..1424d37 100644 --- a/miplearn/components/objective.py +++ b/miplearn/components/objective.py @@ -12,7 +12,7 @@ from miplearn.classifiers import Regressor from miplearn.classifiers.sklearn import ScikitLearnRegressor from miplearn.components.component import Component from miplearn.features import TrainingSample, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.types import LearningSolveStats if TYPE_CHECKING: diff --git a/miplearn/components/primal.py b/miplearn/components/primal.py index 49f56fc..1af4915 100644 --- a/miplearn/components/primal.py +++ b/miplearn/components/primal.py @@ -21,7 +21,7 @@ from miplearn.classifiers.threshold import MinPrecisionThreshold, Threshold from miplearn.components import classifier_evaluation_dict from miplearn.components.component import Component from miplearn.features import TrainingSample, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.types import ( Solution, LearningSolveStats, diff --git a/miplearn/features.py b/miplearn/features.py index 52f2fb7..5d8c2f2 100644 --- a/miplearn/features.py +++ b/miplearn/features.py @@ -11,7 +11,7 @@ from miplearn.types import VarIndex, Solution if TYPE_CHECKING: from miplearn.solvers.internal import InternalSolver - from miplearn.instance import Instance + from miplearn.instance.base import Instance @dataclass diff --git a/miplearn/instance/__init__.py b/miplearn/instance/__init__.py new file mode 100644 index 0000000..5fbccb1 --- /dev/null +++ b/miplearn/instance/__init__.py @@ -0,0 +1,3 @@ +# 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. diff --git a/miplearn/instance.py b/miplearn/instance/base.py similarity index 61% rename from miplearn/instance.py rename to miplearn/instance/base.py index 48a71ba..386ac55 100644 --- a/miplearn/instance.py +++ b/miplearn/instance/base.py @@ -2,12 +2,9 @@ # Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -import gzip import logging -import os -import pickle from abc import ABC, abstractmethod -from typing import Any, List, Optional, Hashable, IO, cast +from typing import Any, List, Optional, Hashable from miplearn.features import TrainingSample, Features from miplearn.types import VarIndex @@ -15,29 +12,11 @@ from miplearn.types import VarIndex logger = logging.getLogger(__name__) -def write_pickle_gz(obj: Any, filename: str) -> None: - logger.info(f"Writing: {filename}") - os.makedirs(os.path.dirname(filename), exist_ok=True) - with gzip.GzipFile(filename, "wb") as file: - pickle.dump(obj, cast(IO[bytes], file)) - - -def read_pickle_gz(filename: str) -> Any: - logger.info(f"Reading: {filename}") - with gzip.GzipFile(filename, "rb") as file: - return pickle.load(cast(IO[bytes], file)) - - -def write_pickle_gz_multiple(objs: List[Any], dirname: str) -> None: - for (i, obj) in enumerate(objs): - write_pickle_gz(obj, f"{dirname}/{i:05d}.pkl.gz") - - # noinspection PyMethodMayBeStatic class Instance(ABC): """ Abstract class holding all the data necessary to generate a concrete model of the - problem. + proble. In the knapsack problem, for example, this class could hold the number of items, their weights and costs, as well as the size of the knapsack. Objects @@ -184,111 +163,3 @@ class Instance(ABC): Save any pending changes made to the instance to the underlying data store. """ pass - - -def lazy_load(func): - def inner(self, *args): - if self.instance is None: - self.instance = self._load() - self.features = self.instance.features - self.training_data = self.instance.training_data - return func(self, *args) - - return inner - - -class PickleGzInstance(Instance): - """ - An instance backed by a gzipped pickle file. - - The instance is only loaded to memory after an operation is called (for example, - `to_model`). - - Parameters - ---------- - filename: str - Path of the gzipped pickle file that should be loaded. - """ - - # noinspection PyMissingConstructor - def __init__(self, filename: str) -> None: - assert os.path.exists(filename), f"File not found: {filename}" - self.instance: Optional[Instance] = None - self.filename: str = filename - - @lazy_load - def to_model(self) -> Any: - assert self.instance is not None - return self.instance.to_model() - - @lazy_load - def get_instance_features(self) -> List[float]: - assert self.instance is not None - return self.instance.get_instance_features() - - @lazy_load - def get_variable_features(self, var_name: str, index: VarIndex) -> List[float]: - assert self.instance is not None - return self.instance.get_variable_features(var_name, index) - - @lazy_load - def get_variable_category( - self, - var_name: str, - index: VarIndex, - ) -> Optional[Hashable]: - assert self.instance is not None - return self.instance.get_variable_category(var_name, index) - - @lazy_load - def get_constraint_features(self, cid: str) -> Optional[List[float]]: - assert self.instance is not None - return self.instance.get_constraint_features(cid) - - @lazy_load - def get_constraint_category(self, cid: str) -> Optional[Hashable]: - assert self.instance is not None - return self.instance.get_constraint_category(cid) - - @lazy_load - def has_static_lazy_constraints(self) -> bool: - assert self.instance is not None - return self.instance.has_static_lazy_constraints() - - @lazy_load - def has_dynamic_lazy_constraints(self): - assert self.instance is not None - return self.instance.has_dynamic_lazy_constraints() - - @lazy_load - def is_constraint_lazy(self, cid: str) -> bool: - assert self.instance is not None - return self.instance.is_constraint_lazy(cid) - - @lazy_load - def find_violated_lazy_constraints(self, model): - assert self.instance is not None - return self.instance.find_violated_lazy_constraints(model) - - @lazy_load - def build_lazy_constraint(self, model, violation): - assert self.instance is not None - return self.instance.build_lazy_constraint(model, violation) - - @lazy_load - def find_violated_user_cuts(self, model): - assert self.instance is not None - return self.instance.find_violated_user_cuts(model) - - @lazy_load - def build_user_cut(self, model, violation): - assert self.instance is not None - return self.instance.build_user_cut(model, violation) - - def _load(self) -> Instance: - obj = read_pickle_gz(self.filename) - assert isinstance(obj, Instance) - return obj - - def flush(self) -> None: - write_pickle_gz(self.instance, self.filename) diff --git a/miplearn/instance/picklegz.py b/miplearn/instance/picklegz.py new file mode 100644 index 0000000..272549b --- /dev/null +++ b/miplearn/instance/picklegz.py @@ -0,0 +1,137 @@ +# 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. + +import gzip +import os +import pickle +from typing import Optional, Any, List, Hashable, cast, IO + +from miplearn.instance.base import logger, Instance +from miplearn.types import VarIndex + + +def lazy_load(func): + def inner(self, *args): + if self.instance is None: + self.instance = self._load() + self.features = self.instance.features + self.training_data = self.instance.training_data + return func(self, *args) + + return inner + + +class PickleGzInstance(Instance): + """ + An instance backed by a gzipped pickle file. + + The instance is only loaded to memory after an operation is called (for example, + `to_model`). + + Parameters + ---------- + filename: str + Path of the gzipped pickle file that should be loaded. + """ + + # noinspection PyMissingConstructor + def __init__(self, filename: str) -> None: + assert os.path.exists(filename), f"File not found: {filename}" + self.instance: Optional[Instance] = None + self.filename: str = filename + + @lazy_load + def to_model(self) -> Any: + assert self.instance is not None + return self.instance.to_model() + + @lazy_load + def get_instance_features(self) -> List[float]: + assert self.instance is not None + return self.instance.get_instance_features() + + @lazy_load + def get_variable_features(self, var_name: str, index: VarIndex) -> List[float]: + assert self.instance is not None + return self.instance.get_variable_features(var_name, index) + + @lazy_load + def get_variable_category( + self, + var_name: str, + index: VarIndex, + ) -> Optional[Hashable]: + assert self.instance is not None + return self.instance.get_variable_category(var_name, index) + + @lazy_load + def get_constraint_features(self, cid: str) -> Optional[List[float]]: + assert self.instance is not None + return self.instance.get_constraint_features(cid) + + @lazy_load + def get_constraint_category(self, cid: str) -> Optional[Hashable]: + assert self.instance is not None + return self.instance.get_constraint_category(cid) + + @lazy_load + def has_static_lazy_constraints(self) -> bool: + assert self.instance is not None + return self.instance.has_static_lazy_constraints() + + @lazy_load + def has_dynamic_lazy_constraints(self): + assert self.instance is not None + return self.instance.has_dynamic_lazy_constraints() + + @lazy_load + def is_constraint_lazy(self, cid: str) -> bool: + assert self.instance is not None + return self.instance.is_constraint_lazy(cid) + + @lazy_load + def find_violated_lazy_constraints(self, model): + assert self.instance is not None + return self.instance.find_violated_lazy_constraints(model) + + @lazy_load + def build_lazy_constraint(self, model, violation): + assert self.instance is not None + return self.instance.build_lazy_constraint(model, violation) + + @lazy_load + def find_violated_user_cuts(self, model): + assert self.instance is not None + return self.instance.find_violated_user_cuts(model) + + @lazy_load + def build_user_cut(self, model, violation): + assert self.instance is not None + return self.instance.build_user_cut(model, violation) + + def _load(self) -> Instance: + obj = read_pickle_gz(self.filename) + assert isinstance(obj, Instance) + return obj + + def flush(self) -> None: + write_pickle_gz(self.instance, self.filename) + + +def write_pickle_gz(obj: Any, filename: str) -> None: + logger.info(f"Writing: {filename}") + os.makedirs(os.path.dirname(filename), exist_ok=True) + with gzip.GzipFile(filename, "wb") as file: + pickle.dump(obj, cast(IO[bytes], file)) + + +def read_pickle_gz(filename: str) -> Any: + logger.info(f"Reading: {filename}") + with gzip.GzipFile(filename, "rb") as file: + return pickle.load(cast(IO[bytes], file)) + + +def write_pickle_gz_multiple(objs: List[Any], dirname: str) -> None: + for (i, obj) in enumerate(objs): + write_pickle_gz(obj, f"{dirname}/{i:05d}.pkl.gz") diff --git a/miplearn/problems/knapsack.py b/miplearn/problems/knapsack.py index ba5bc3e..981180e 100644 --- a/miplearn/problems/knapsack.py +++ b/miplearn/problems/knapsack.py @@ -8,7 +8,7 @@ import pyomo.environ as pe from scipy.stats import uniform, randint from scipy.stats.distributions import rv_frozen -from miplearn.instance import Instance +from miplearn.instance.base import Instance class ChallengeA: diff --git a/miplearn/problems/stab.py b/miplearn/problems/stab.py index 9971a91..fcb4f05 100644 --- a/miplearn/problems/stab.py +++ b/miplearn/problems/stab.py @@ -8,7 +8,7 @@ import pyomo.environ as pe from scipy.stats import uniform, randint from scipy.stats.distributions import rv_frozen -from miplearn.instance import Instance +from miplearn.instance.base import Instance class ChallengeA: diff --git a/miplearn/problems/tsp.py b/miplearn/problems/tsp.py index e5b4ef4..6b07673 100644 --- a/miplearn/problems/tsp.py +++ b/miplearn/problems/tsp.py @@ -9,7 +9,7 @@ from scipy.spatial.distance import pdist, squareform from scipy.stats import uniform, randint from scipy.stats.distributions import rv_frozen -from miplearn.instance import Instance +from miplearn.instance.base import Instance class ChallengeA: diff --git a/miplearn/solvers/gurobi.py b/miplearn/solvers/gurobi.py index 2f4b632..3234074 100644 --- a/miplearn/solvers/gurobi.py +++ b/miplearn/solvers/gurobi.py @@ -8,7 +8,7 @@ from io import StringIO from random import randint from typing import List, Any, Dict, Optional, cast, Tuple, Union -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers import _RedirectOutput from miplearn.solvers.internal import ( InternalSolver, diff --git a/miplearn/solvers/internal.py b/miplearn/solvers/internal.py index bd73df7..befbbb3 100644 --- a/miplearn/solvers/internal.py +++ b/miplearn/solvers/internal.py @@ -6,7 +6,7 @@ import logging from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.types import ( LPSolveStats, IterationCallback, diff --git a/miplearn/solvers/learning.py b/miplearn/solvers/learning.py index 5d8431a..d913890 100644 --- a/miplearn/solvers/learning.py +++ b/miplearn/solvers/learning.py @@ -14,7 +14,8 @@ from miplearn.components.dynamic_user_cuts import UserCutsComponent from miplearn.components.objective import ObjectiveValueComponent from miplearn.components.primal import PrimalSolutionComponent from miplearn.features import FeaturesExtractor, TrainingSample -from miplearn.instance import Instance, PickleGzInstance +from miplearn.instance.base import Instance +from miplearn.instance.picklegz import PickleGzInstance from miplearn.solvers import _RedirectOutput from miplearn.solvers.internal import InternalSolver from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver diff --git a/miplearn/solvers/pyomo/base.py b/miplearn/solvers/pyomo/base.py index eba8a14..f442163 100644 --- a/miplearn/solvers/pyomo/base.py +++ b/miplearn/solvers/pyomo/base.py @@ -14,7 +14,7 @@ from pyomo.core import Var, Constraint from pyomo.opt import TerminationCondition from pyomo.opt.base.solvers import SolverFactory -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers import _RedirectOutput from miplearn.solvers.internal import ( InternalSolver, diff --git a/tests/components/steps/test_convert_tight.py b/tests/components/steps/test_convert_tight.py index 48134ab..bc4d8c0 100644 --- a/tests/components/steps/test_convert_tight.py +++ b/tests/components/steps/test_convert_tight.py @@ -7,7 +7,7 @@ from unittest.mock import Mock from miplearn.classifiers import Classifier from miplearn.components.steps.convert_tight import ConvertTightIneqsIntoEqsStep from miplearn.components.steps.relax_integrality import RelaxIntegralityStep -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.problems.knapsack import GurobiKnapsackInstance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.learning import LearningSolver diff --git a/tests/components/steps/test_drop_redundant.py b/tests/components/steps/test_drop_redundant.py index 74bb7ea..bddfb95 100644 --- a/tests/components/steps/test_drop_redundant.py +++ b/tests/components/steps/test_drop_redundant.py @@ -10,7 +10,7 @@ from miplearn.classifiers import Classifier from miplearn.components.steps.drop_redundant import DropRedundantInequalitiesStep from miplearn.components.steps.relax_integrality import RelaxIntegralityStep from miplearn.features import TrainingSample, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.internal import InternalSolver from miplearn.solvers.learning import LearningSolver diff --git a/tests/components/test_component.py b/tests/components/test_component.py index aeb8ad8..5fd00d7 100644 --- a/tests/components/test_component.py +++ b/tests/components/test_component.py @@ -4,7 +4,7 @@ from unittest.mock import Mock from miplearn.components.component import Component -from miplearn.instance import Instance +from miplearn.instance.base import Instance def test_xy_instance(): diff --git a/tests/components/test_dynamic_lazy.py b/tests/components/test_dynamic_lazy.py index ac0eafd..ce5c8cc 100644 --- a/tests/components/test_dynamic_lazy.py +++ b/tests/components/test_dynamic_lazy.py @@ -17,7 +17,7 @@ from miplearn.features import ( Features, InstanceFeatures, ) -from miplearn.instance import Instance +from miplearn.instance.base import Instance E = 0.1 diff --git a/tests/components/test_dynamic_user_cuts.py b/tests/components/test_dynamic_user_cuts.py index 1fa5da0..7ca7fc3 100644 --- a/tests/components/test_dynamic_user_cuts.py +++ b/tests/components/test_dynamic_user_cuts.py @@ -12,7 +12,7 @@ from gurobipy import GRB from networkx import Graph from miplearn.components.dynamic_user_cuts import UserCutsComponent -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.learning import LearningSolver diff --git a/tests/components/test_objective.py b/tests/components/test_objective.py index 07106bf..b5f3ce2 100644 --- a/tests/components/test_objective.py +++ b/tests/components/test_objective.py @@ -11,7 +11,7 @@ from numpy.testing import assert_array_equal from miplearn.classifiers import Regressor from miplearn.components.objective import ObjectiveValueComponent from miplearn.features import TrainingSample, InstanceFeatures, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.learning import LearningSolver from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver from tests.fixtures.knapsack import get_knapsack_instance diff --git a/tests/components/test_primal.py b/tests/components/test_primal.py index a1dbe94..58719ed 100644 --- a/tests/components/test_primal.py +++ b/tests/components/test_primal.py @@ -12,7 +12,7 @@ from miplearn.classifiers.threshold import Threshold from miplearn.components import classifier_evaluation_dict from miplearn.components.primal import PrimalSolutionComponent from miplearn.features import TrainingSample, VariableFeatures, Features -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.problems.tsp import TravelingSalesmanGenerator from miplearn.solvers.learning import LearningSolver diff --git a/tests/components/test_static_lazy.py b/tests/components/test_static_lazy.py index fbb8045..b4cd5d7 100644 --- a/tests/components/test_static_lazy.py +++ b/tests/components/test_static_lazy.py @@ -17,7 +17,7 @@ from miplearn.features import ( ConstraintFeatures, Features, ) -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.internal import InternalSolver from miplearn.solvers.learning import LearningSolver from miplearn.types import ( diff --git a/tests/fixtures/infeasible.py b/tests/fixtures/infeasible.py index 65dac00..6fdee4a 100644 --- a/tests/fixtures/infeasible.py +++ b/tests/fixtures/infeasible.py @@ -6,7 +6,7 @@ from typing import Any from pyomo import environ as pe -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.pyomo.base import BasePyomoSolver from tests.solvers import _is_subclass_or_instance diff --git a/tests/fixtures/knapsack.py b/tests/fixtures/knapsack.py index 8e55af9..8013d5f 100644 --- a/tests/fixtures/knapsack.py +++ b/tests/fixtures/knapsack.py @@ -1,7 +1,7 @@ # 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 miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.internal import InternalSolver diff --git a/tests/fixtures/redundant.py b/tests/fixtures/redundant.py index e714cb5..4deeab0 100644 --- a/tests/fixtures/redundant.py +++ b/tests/fixtures/redundant.py @@ -5,7 +5,7 @@ from typing import Any import pyomo.environ as pe -from miplearn.instance import Instance +from miplearn.instance.base import Instance from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.pyomo.base import BasePyomoSolver from tests.solvers import _is_subclass_or_instance diff --git a/tests/instance/__init__.py b/tests/instance/__init__.py new file mode 100644 index 0000000..5fbccb1 --- /dev/null +++ b/tests/instance/__init__.py @@ -0,0 +1,3 @@ +# 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. diff --git a/tests/test_instance.py b/tests/instance/test_picklegz.py similarity index 85% rename from tests/test_instance.py rename to tests/instance/test_picklegz.py index 2713f7b..dfff69d 100644 --- a/tests/test_instance.py +++ b/tests/instance/test_picklegz.py @@ -3,12 +3,12 @@ # Released under the modified BSD license. See COPYING.md for more details. import tempfile -from miplearn.instance import write_pickle_gz, PickleGzInstance +from miplearn.instance.picklegz import write_pickle_gz, PickleGzInstance from miplearn.solvers.gurobi import GurobiSolver from tests.fixtures.knapsack import get_knapsack_instance -def test_pickled() -> None: +def test_usage() -> None: original = get_knapsack_instance(GurobiSolver()) file = tempfile.NamedTemporaryFile() write_pickle_gz(original, file.name) diff --git a/tests/solvers/test_learning_solver.py b/tests/solvers/test_learning_solver.py index 0c8a2a7..5e1ad2a 100644 --- a/tests/solvers/test_learning_solver.py +++ b/tests/solvers/test_learning_solver.py @@ -8,7 +8,7 @@ import tempfile import dill -from miplearn.instance import PickleGzInstance, write_pickle_gz, read_pickle_gz +from miplearn.instance.picklegz import PickleGzInstance, write_pickle_gz, read_pickle_gz from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.learning import LearningSolver from . import _get_knapsack_instance, get_internal_solvers