# Module `miplearn.extractors`

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 gzip
    import logging
    import pickle
    from abc import ABC, abstractmethod

    import numpy as np
    from tqdm.auto import tqdm

    logger = logging.getLogger(__name__)

    class InstanceIterator:
        def __init__(self, instances):
            self.instances = instances
            self.current = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self.current >= len(self.instances):
                raise StopIteration
            result = self.instances[self.current]
            self.current += 1
            if isinstance(result, str):
                logger.debug("Read: %s" % result)
                try:
                    if result.endswith(".gz"):
                        with gzip.GzipFile(result, "rb") as file:
                            result = pickle.load(file)
                    else:
                        with open(result, "rb") as file:
                            result = pickle.load(file)
                except pickle.UnpicklingError:
                    raise Exception(f"Invalid instance file: {result}")
            return result

    class Extractor(ABC):
        @abstractmethod
        def extract(self, instances):
            pass

        @staticmethod
        def split_variables(instance):
            result = {}
            lp_solution = instance.training_data[0]["LP solution"]
            for var_name in lp_solution:
                for index in lp_solution[var_name]:
                    category = instance.get_variable_category(var_name, index)
                    if category is None:
                        continue
                    if category not in result:
                        result[category] = []
                    result[category] += [(var_name, index)]
            return result

    class VariableFeaturesExtractor(Extractor):
        def extract(self, instances):
            result = {}
            for instance in tqdm(
                InstanceIterator(instances),
                desc="Extract (vars)",
                disable=len(instances) < 5,
            ):
                instance_features = instance.get_instance_features()
                var_split = self.split_variables(instance)
                lp_solution = instance.training_data[0]["LP solution"]
                for (category, var_index_pairs) in var_split.items():
                    if category not in result:
                        result[category] = []
                    for (var_name, index) in var_index_pairs:
                        result[category] += [
                            instance_features.tolist()
                            + instance.get_variable_features(var_name, index).tolist()
                            + [lp_solution[var_name][index]]
                        ]
            for category in result:
                result[category] = np.array(result[category])
            return result

    class SolutionExtractor(Extractor):
        def __init__(self, relaxation=False):
            self.relaxation = relaxation

        def extract(self, instances):
            result = {}
            for instance in tqdm(
                InstanceIterator(instances),
                desc="Extract (solution)",
                disable=len(instances) < 5,
            ):
                var_split = self.split_variables(instance)
                if self.relaxation:
                    solution = instance.training_data[0]["LP solution"]
                else:
                    solution = instance.training_data[0]["Solution"]
                for (category, var_index_pairs) in var_split.items():
                    if category not in result:
                        result[category] = []
                    for (var_name, index) in var_index_pairs:
                        v = solution[var_name][index]
                        if v is None:
                            result[category] += [[0, 0]]
                        else:
                            result[category] += [[1 - v, v]]
            for category in result:
                result[category] = np.array(result[category])
            return result

    class InstanceFeaturesExtractor(Extractor):
        def extract(self, instances):
            return np.vstack(
                [
                    np.hstack(
                        [
                            instance.get_instance_features(),
                            instance.training_data[0]["LP value"],
                        ]
                    )
                    for instance in InstanceIterator(instances)
                ]
            )

    class ObjectiveValueExtractor(Extractor):
        def __init__(self, kind="lp"):
            assert kind in ["lower bound", "upper bound", "lp"]
            self.kind = kind

        def extract(self, instances):
            if self.kind == "lower bound":
                return np.array(
                    [
                        [instance.training_data[0]["Lower bound"]]
                        for instance in InstanceIterator(instances)
                    ]
                )
            if self.kind == "upper bound":
                return np.array(
                    [
                        [instance.training_data[0]["Upper bound"]]
                        for instance in InstanceIterator(instances)
                    ]
                )
            if self.kind == "lp":
                return np.array(
                    [
                        [instance.training_data[0]["LP value"]]
                        for instance in InstanceIterator(instances)
                    ]
                )

## Classes

`class Extractor`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class Extractor(ABC):
          @abstractmethod
          def extract(self, instances):
              pass

          @staticmethod
          def split_variables(instance):
              result = {}
              lp_solution = instance.training_data[0]["LP solution"]
              for var_name in lp_solution:
                  for index in lp_solution[var_name]:
                      category = instance.get_variable_category(var_name, index)
                      if category is None:
                          continue
                      if category not in result:
                          result[category] = []
                      result[category] += [(var_name, index)]
              return result

  ### Ancestors

  - abc.ABC

  ### Subclasses

  - InstanceFeaturesExtractor
  - ObjectiveValueExtractor
  - SolutionExtractor
  - VariableFeaturesExtractor

  ### Static methods

  `def split_variables(instance)`
    Expand source code

        @staticmethod
        def split_variables(instance):
            result = {}
            lp_solution = instance.training_data[0]["LP solution"]
            for var_name in lp_solution:
                for index in lp_solution[var_name]:
                    category = instance.get_variable_category(var_name, index)
                    if category is None:
                        continue
                    if category not in result:
                        result[category] = []
                    result[category] += [(var_name, index)]
            return result

  ### Methods

  `def extract(self, instances)`
    Expand source code

        @abstractmethod
        def extract(self, instances):
            pass

`class InstanceFeaturesExtractor`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class InstanceFeaturesExtractor(Extractor):
          def extract(self, instances):
              return np.vstack(
                  [
                      np.hstack(
                          [
                              instance.get_instance_features(),
                              instance.training_data[0]["LP value"],
                          ]
                      )
                      for instance in InstanceIterator(instances)
                  ]
              )

  ### Ancestors

  - Extractor
  - abc.ABC

  ### Methods

  `def extract(self, instances)`
    Expand source code

        def extract(self, instances):
            return np.vstack(
                [
                    np.hstack(
                        [
                            instance.get_instance_features(),
                            instance.training_data[0]["LP value"],
                        ]
                    )
                    for instance in InstanceIterator(instances)
                ]
            )

`class InstanceIterator (instances)`
  Expand source code

      class InstanceIterator:
          def __init__(self, instances):
              self.instances = instances
              self.current = 0

          def __iter__(self):
              return self

          def __next__(self):
              if self.current >= len(self.instances):
                  raise StopIteration
              result = self.instances[self.current]
              self.current += 1
              if isinstance(result, str):
                  logger.debug("Read: %s" % result)
                  try:
                      if result.endswith(".gz"):
                          with gzip.GzipFile(result, "rb") as file:
                              result = pickle.load(file)
                      else:
                          with open(result, "rb") as file:
                              result = pickle.load(file)
                  except pickle.UnpicklingError:
                      raise Exception(f"Invalid instance file: {result}")
              return result

`class ObjectiveValueExtractor (kind='lp')`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class ObjectiveValueExtractor(Extractor):
          def __init__(self, kind="lp"):
              assert kind in ["lower bound", "upper bound", "lp"]
              self.kind = kind

          def extract(self, instances):
              if self.kind == "lower bound":
                  return np.array(
                      [
                          [instance.training_data[0]["Lower bound"]]
                          for instance in InstanceIterator(instances)
                      ]
                  )
              if self.kind == "upper bound":
                  return np.array(
                      [
                          [instance.training_data[0]["Upper bound"]]
                          for instance in InstanceIterator(instances)
                      ]
                  )
              if self.kind == "lp":
                  return np.array(
                      [
                          [instance.training_data[0]["LP value"]]
                          for instance in InstanceIterator(instances)
                      ]
                  )

  ### Ancestors

  - Extractor
  - abc.ABC

  ### Methods

  `def extract(self, instances)`
    Expand source code

        def extract(self, instances):
            if self.kind == "lower bound":
                return np.array(
                    [
                        [instance.training_data[0]["Lower bound"]]
                        for instance in InstanceIterator(instances)
                    ]
                )
            if self.kind == "upper bound":
                return np.array(
                    [
                        [instance.training_data[0]["Upper bound"]]
                        for instance in InstanceIterator(instances)
                    ]
                )
            if self.kind == "lp":
                return np.array(
                    [
                        [instance.training_data[0]["LP value"]]
                        for instance in InstanceIterator(instances)
                    ]
                )

`class SolutionExtractor (relaxation=False)`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class SolutionExtractor(Extractor):
          def __init__(self, relaxation=False):
              self.relaxation = relaxation

          def extract(self, instances):
              result = {}
              for instance in tqdm(
                  InstanceIterator(instances),
                  desc="Extract (solution)",
                  disable=len(instances) < 5,
              ):
                  var_split = self.split_variables(instance)
                  if self.relaxation:
                      solution = instance.training_data[0]["LP solution"]
                  else:
                      solution = instance.training_data[0]["Solution"]
                  for (category, var_index_pairs) in var_split.items():
                      if category not in result:
                          result[category] = []
                      for (var_name, index) in var_index_pairs:
                          v = solution[var_name][index]
                          if v is None:
                              result[category] += [[0, 0]]
                          else:
                              result[category] += [[1 - v, v]]
              for category in result:
                  result[category] = np.array(result[category])
              return result

  ### Ancestors

  - Extractor
  - abc.ABC

  ### Methods

  `def extract(self, instances)`
    Expand source code

        def extract(self, instances):
            result = {}
            for instance in tqdm(
                InstanceIterator(instances),
                desc="Extract (solution)",
                disable=len(instances) < 5,
            ):
                var_split = self.split_variables(instance)
                if self.relaxation:
                    solution = instance.training_data[0]["LP solution"]
                else:
                    solution = instance.training_data[0]["Solution"]
                for (category, var_index_pairs) in var_split.items():
                    if category not in result:
                        result[category] = []
                    for (var_name, index) in var_index_pairs:
                        v = solution[var_name][index]
                        if v is None:
                            result[category] += [[0, 0]]
                        else:
                            result[category] += [[1 - v, v]]
            for category in result:
                result[category] = np.array(result[category])
            return result

`class VariableFeaturesExtractor`

  Helper class that provides a standard way to create an ABC using inheritance.

  Expand source code

      class VariableFeaturesExtractor(Extractor):
          def extract(self, instances):
              result = {}
              for instance in tqdm(
                  InstanceIterator(instances),
                  desc="Extract (vars)",
                  disable=len(instances) < 5,
              ):
                  instance_features = instance.get_instance_features()
                  var_split = self.split_variables(instance)
                  lp_solution = instance.training_data[0]["LP solution"]
                  for (category, var_index_pairs) in var_split.items():
                      if category not in result:
                          result[category] = []
                      for (var_name, index) in var_index_pairs:
                          result[category] += [
                              instance_features.tolist()
                              + instance.get_variable_features(var_name, index).tolist()
                              + [lp_solution[var_name][index]]
                          ]
              for category in result:
                  result[category] = np.array(result[category])
              return result

  ### Ancestors

  - Extractor
  - abc.ABC

  ### Methods

  `def extract(self, instances)`
    Expand source code

        def extract(self, instances):
            result = {}
            for instance in tqdm(
                InstanceIterator(instances),
                desc="Extract (vars)",
                disable=len(instances) < 5,
            ):
                instance_features = instance.get_instance_features()
                var_split = self.split_variables(instance)
                lp_solution = instance.training_data[0]["LP solution"]
                for (category, var_index_pairs) in var_split.items():
                    if category not in result:
                        result[category] = []
                    for (var_name, index) in var_index_pairs:
                        result[category] += [
                            instance_features.tolist()
                            + instance.get_variable_features(var_name, index).tolist()
                            + [lp_solution[var_name][index]]
                        ]
            for category in result:
                result[category] = np.array(result[category])
            return result