SimonBlanke/Hyperactive

View on GitHub
hyperactive/search_space.py

Summary

Maintainability
A
1 hr
Test Coverage
# Author: Simon Blanke
# Email: simon.blanke@yahoo.com
# License: MIT License

import numpy as np


class DictClass:
    def __init__(self, search_space):
        self.search_space = search_space

    def __getitem__(self, key):
        return self.search_space[key]

    def keys(self):
        return self.search_space.keys()

    def values(self):
        return self.search_space.values()


class SearchSpace(DictClass):
    def __init__(self, search_space):
        super().__init__(search_space)
        self.search_space = search_space

        self.dim_keys = list(search_space.keys())
        self.values_l = list(self.search_space.values())

        positions = {}
        for key in search_space.keys():
            positions[key] = np.array(range(len(search_space[key])))
        self.positions = positions

        self.check_list()
        self.check_non_num_values()

        self.data_types = self.dim_types()
        self.func2str = self._create_num_str_ss()

    def __call__(self):
        return self.search_space

    def dim_types(self):
        data_types = {}
        for dim_key in self.dim_keys:
            dim_values = np.array(list(self.search_space[dim_key]))
            try:
                np.subtract(dim_values, dim_values)
                np.array(dim_values).searchsorted(dim_values)
            except:
                _type_ = "object"
            else:
                _type_ = "number"

            data_types[dim_key] = _type_
        return data_types

    def _create_num_str_ss(self):
        func2str = {}
        for dim_key in self.dim_keys:
            if self.data_types[dim_key] == "number":
                func2str[dim_key] = self.search_space[dim_key]
            else:
                func2str[dim_key] = []

                dim_values = self.search_space[dim_key]
                for value in dim_values:
                    try:
                        func_name = value.__name__
                    except:
                        func_name = value

                    func2str[dim_key].append(func_name)
        return func2str

    def check_list(self):
        for dim_key in self.dim_keys:
            search_dim = self.search_space[dim_key]

            err_msg = "\n Value in '{}' of search space dictionary must be of type list \n".format(
                dim_key
            )
            if not isinstance(search_dim, list):
                raise ValueError(err_msg)

    @staticmethod
    def is_function(value):
        try:
            value.__name__
        except:
            return False
        else:
            return True

    @staticmethod
    def is_number(value):
        try:
            float(value)
            value * 0.1
            value - 0.1
            value / 0.1
        except:
            return False
        else:
            return True

    def _string_or_object(self, dim_key, dim_values):
        for dim_value in dim_values:
            is_str = isinstance(dim_value, str)
            is_func = self.is_function(dim_value)
            is_number = self.is_number(dim_value)

            if not is_str and not is_func and not is_number:
                msg = "\n The value '{}' of type '{}' in the search space dimension '{}' must be number, string or function \n".format(
                    dim_value, type(dim_value), dim_key
                )
                raise ValueError(msg)

    def check_non_num_values(self):
        for dim_key in self.dim_keys:
            dim_values = np.array(list(self.search_space[dim_key]))

            try:
                np.subtract(dim_values, dim_values)
                np.array(dim_values).searchsorted(dim_values)
            except:
                self._string_or_object(dim_key, dim_values)
            else:
                if dim_values.ndim != 1:
                    msg = "Array-like object in '{}' must be one dimensional".format(
                        dim_key
                    )
                    raise ValueError(msg)