Esempio n. 1
0
    def build_from(self, cmd_args):
        """Create a definition of the problem's search space, using information
        from the user's script configuration (if provided) and command line arguments.

        :param cmd_args: A list of command line arguments provided for the user's script.

        :rtype: `metaopt.algo.space.Space`

        .. note:: A template configuration file complementing user's script can be
           provided either by explicitly using the prefix '--config=' or by being the
           first positional argument.

        """
        self.userargs_tmpl = None
        self.userconfig_tmpl = None
        self.space = Space()

        self.userconfig, self.is_userconfig_an_option = self._build_from_args(
            cmd_args)

        if self.userconfig:
            self._build_from_config(self.userconfig)

        log.debug(
            "Configuration and command line arguments were parsed and "
            "a `Space` object was built successfully:\n%s", self.space)

        return self.space
Esempio n. 2
0
    def test_interval(self):
        """Check whether interval is cool."""
        space = Space()
        probs = (0.1, 0.2, 0.3, 0.4)
        categories = ('asdfa', 2, 3, 4)
        dim = Categorical('yolo', OrderedDict(zip(categories, probs)), shape=2)
        space.register(dim)
        dim = Integer('yolo2', 'uniform', -3, 6)
        space.register(dim)
        dim = Real('yolo3', 'norm', 0.9)
        space.register(dim)

        assert space.interval() == [categories, (-3, 3), (-np.inf, np.inf)]
Esempio n. 3
0
def space():
    """Construct a simple space with every possible kind of Dimension."""
    space = Space()
    categories = {'asdfa': 0.1, 2: 0.2, 3: 0.3, 4: 0.4}
    dim = Categorical('yolo', categories, shape=2)
    space.register(dim)
    dim = Integer('yolo2', 'uniform', -3, 6)
    space.register(dim)
    dim = Real('yolo3', 'alpha', 0.9)
    space.register(dim)
    return space
Esempio n. 4
0
    def test_sample(self, seed):
        """Check whether sampling works correctly."""
        space = Space()
        probs = (0.1, 0.2, 0.3, 0.4)
        categories = ('asdfa', 2, 3, 4)
        dim1 = Categorical('yolo',
                           OrderedDict(zip(categories, probs)),
                           shape=(2, 2))
        space.register(dim1)
        dim2 = Integer('yolo2', 'uniform', -3, 6)
        space.register(dim2)
        dim3 = Real('yolo3', 'norm', 0.9)
        space.register(dim3)

        point = space.sample(seed=seed)
        test_point = [
            (dim1.sample()[0], dim2.sample()[0], dim3.sample()[0]),
        ]
        assert len(point) == len(test_point) == 1
        assert len(point[0]) == len(test_point[0]) == 3
        assert np.all(point[0][0] == test_point[0][0])
        assert point[0][1] == test_point[0][1]
        assert point[0][2] == test_point[0][2]

        points = space.sample(2, seed=seed)
        points1 = dim1.sample(2)
        points2 = dim2.sample(2)
        points3 = dim3.sample(2)
        test_points = [(points1[0], points2[0], points3[0]),
                       (points1[1], points2[1], points3[1])]
        assert len(points) == len(test_points) == 2
        for i in range(2):
            assert len(points[i]) == len(test_points[i]) == 3
            assert np.all(points[i][0] == test_points[i][0])
            assert points[i][1] == test_points[i][1]
            assert points[i][2] == test_points[i][2]
Esempio n. 5
0
    def test_repr(self):
        """Test str/repr."""
        space = Space()
        dim = Integer('yolo2', 'uniform', -3, 6, shape=(2, ))
        space.register(dim)
        dim = Real('yolo3', 'norm', 0.9)
        space.register(dim)

        assert str(space) == "Space(["\
                             "Integer(name=yolo2, prior={uniform: (-3, 6), {}}, shape=(2,)),\n" \
                             "       Real(name=yolo3, prior={norm: (0.9,), {}}, shape=())])"
Esempio n. 6
0
    def test_getitem(self):
        """Test getting dimensions from space."""
        space = Space()
        probs = (0.1, 0.2, 0.3, 0.4)
        categories = ('asdfa', 2, 3, 4)
        dim = Categorical('yolo', OrderedDict(zip(categories, probs)), shape=2)
        space.register(dim)
        dim = Integer('yolo2', 'uniform', -3, 6)
        space.register(dim)
        dim = Real('yolo3', 'norm', 0.9)
        space.register(dim)

        assert space['yolo'].type == 'categorical'
        assert space[0].type == 'categorical'

        with pytest.raises(KeyError):
            space['asdf']

        with pytest.raises(IndexError):
            space[3]
Esempio n. 7
0
    def test_register_and_contain(self):
        """Register bunch of dimensions, check if points/name are in space."""
        space = Space()

        assert 'yolo' not in space
        assert (('asdfa', 2), 0, 3.5) not in space

        categories = {'asdfa': 0.1, 2: 0.2, 3: 0.3, 4: 0.4}
        dim = Categorical('yolo', categories, shape=2)
        space.register(dim)
        dim = Integer('yolo2', 'uniform', -3, 6)
        space.register(dim)
        dim = Real('yolo3', 'norm', 0.9)
        space.register(dim)

        assert 'yolo' in space
        assert 'yolo2' in space
        assert 'yolo3' in space

        assert (('asdfa', 2), 0, 3.5) in space
        assert (('asdfa', 2), 7, 3.5) not in space
Esempio n. 8
0
    def test_bad_setitem(self):
        """Check exceptions in setting items in Space."""
        space = Space()

        # The name of an integer must be a of `str` type.
        # Integers are reversed for indexing the OrderedDict.
        with pytest.raises(TypeError) as exc:
            space[5] = Integer('yolo', 'uniform', -3, 6)
        assert "string" in str(exc.value)

        # Only object of type `Dimension` are allowed in `Space`.
        with pytest.raises(TypeError) as exc:
            space['ispis'] = 'nope'
        assert "Dimension" in str(exc.value)

        # Cannot register something with the same name.
        space.register(Integer('yolo', 'uniform', -3, 6))
        with pytest.raises(ValueError) as exc:
            space.register(Real('yolo', 'uniform', 0, 6))
        assert "another name" in str(exc.value)
Esempio n. 9
0
 def test_bad_contain(self):
     """Checking with no iterables does no good."""
     space = Space()
     with pytest.raises(TypeError):
         5 in space
Esempio n. 10
0
 def test_init(self):
     """Instantiate space, must be an ordered dictionary."""
     space = Space()
     assert isinstance(space, OrderedDict)
Esempio n. 11
0
class SpaceBuilder(object, metaclass=SingletonType):
    """Build a `Space` object form user's configuration."""

    USERCONFIG_OPTION = '--config='
    USERCONFIG_KEYWORD = 'mopt~'
    USERARGS_SEARCH = r'\W*([a-zA-Z0-9_-]+)~(.*)'
    USERARGS_TMPL = r'(.*)~(.*)'

    def __init__(self):
        """Initialize a `SpaceBuilder`."""
        self.userconfig = None
        self.is_userconfig_an_option = None
        self.userargs_tmpl = None
        self.userconfig_tmpl = None
        self.dimbuilder = DimensionBuilder()
        self.space = None
        self.converter = None

    def build_from(self, cmd_args):
        """Create a definition of the problem's search space, using information
        from the user's script configuration (if provided) and command line arguments.

        :param cmd_args: A list of command line arguments provided for the user's script.

        :rtype: `metaopt.algo.space.Space`

        .. note:: A template configuration file complementing user's script can be
           provided either by explicitly using the prefix '--config=' or by being the
           first positional argument.

        """
        self.userargs_tmpl = None
        self.userconfig_tmpl = None
        self.space = Space()

        self.userconfig, self.is_userconfig_an_option = self._build_from_args(
            cmd_args)

        if self.userconfig:
            self._build_from_config(self.userconfig)

        log.debug(
            "Configuration and command line arguments were parsed and "
            "a `Space` object was built successfully:\n%s", self.space)

        return self.space

    def _build_from_config(self, config_path):
        self.converter = infer_converter_from_file_type(config_path)
        self.userconfig_tmpl = self.converter.parse(config_path)

        stack = collections.deque()
        stack.append(('', self.userconfig_tmpl))
        while True:
            try:
                namespace, stuff = stack.pop()
            except IndexError:
                break
            if isinstance(stuff, dict):
                for k, v in stuff.items():
                    stack.append(('/'.join([namespace, str(k)]), v))
            elif isinstance(stuff, list):
                for position, thing in enumerate(stuff):
                    stack.append(('/'.join([namespace, str(position)]), thing))
            elif isinstance(stuff, str):
                if stuff.startswith(self.USERCONFIG_KEYWORD):
                    dimension = self.dimbuilder.build(
                        namespace, stuff[len(self.USERCONFIG_KEYWORD):])
                    try:
                        self.space.register(dimension)
                    except ValueError as exc:
                        error_msg = "Conflict for name '{}' in script configuration "\
                                    "and arguments.".format(namespace)
                        raise ValueError(error_msg) from exc

    def _build_from_args(self, cmd_args):
        userconfig = None
        is_userconfig_an_option = None
        self.userargs_tmpl = collections.defaultdict(list)
        args_pattern = re.compile(self.USERARGS_SEARCH)
        args_prefix_pattern = re.compile(self.USERARGS_TMPL)

        for arg in cmd_args:
            found = args_pattern.findall(arg)
            if len(found) != 1:
                if arg.startswith(self.USERCONFIG_OPTION):
                    if not userconfig:
                        userconfig = arg[len(self.USERCONFIG_OPTION):]
                        is_userconfig_an_option = True
                    else:
                        raise ValueError(
                            "Already found one configuration file in: %s" %
                            userconfig)
                else:
                    self.userargs_tmpl[None].append(arg)
                continue

            name, expression = found[0]
            namespace = '/' + name
            dimension = self.dimbuilder.build(namespace, expression)
            self.space.register(dimension)

            found = args_prefix_pattern.findall(arg)
            assert len(found) == 1 and found[0][
                1] == expression, "Parsing prefix problem."
            self.userargs_tmpl[namespace] = found[0][0] + '='

        if not userconfig and self.userargs_tmpl[
                None]:  # try the first positional argument
            if os.path.isfile(self.userargs_tmpl[None][0]):
                userconfig = self.userargs_tmpl[None].pop(0)
                is_userconfig_an_option = False

        return userconfig, is_userconfig_an_option

    def build_to(self, config_path, trial):
        """Use templates saved from `build_from` to generate a config file (if needed)
        and command line arguments to correspond to specific parameter selections.

        :param config_path: Path in which the configuration file instance
           will be created.
        :param trial: A `metaopt.core.worker.trial.Trial` object with concrete
           parameter values for the defined `Space`.

        :returns: A list with the command line arguments that must be given to
           script's execution.

        """
        if self.userconfig:
            self._build_to_config(config_path, trial)
        return self._build_to_args(config_path, trial)

    def _build_to_config(self, config_path, trial):
        config_instance = copy.deepcopy(self.userconfig_tmpl)

        for param in trial.params:
            stuff = config_instance
            path = param.name.split('/')
            for key in path[1:]:
                # Parameter name may correspond to stuff in cmd args
                if isinstance(stuff, list):
                    key = int(key)
                    try:
                        stuff[key]
                    except IndexError:
                        break
                else:  # isinstance(stuff, dict):
                    if key not in stuff:
                        break

                if isinstance(stuff[key], str):
                    stuff[key] = param.value
                else:
                    stuff = stuff[key]

        self.converter.generate(config_path, config_instance)

    def _build_to_args(self, config_path, trial):
        cmd_args = []

        if self.userconfig:
            if self.is_userconfig_an_option:
                cmd_args.append(self.USERCONFIG_OPTION + config_path)
            else:
                cmd_args.append(config_path)

        cmd_args.extend(self.userargs_tmpl[None])

        for param in trial.params:
            if param.name not in self.userargs_tmpl:
                continue
            prefix = self.userargs_tmpl[param.name]
            cmd_args.append(prefix + str(param.value))

        return cmd_args