Passed
Push — master ( f43d66...7e252e )
by Simon
04:41 queued 17s
created

GridSearchOptimizer.grid_move()   A

Complexity

Conditions 2

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 1
dl 0
loc 19
rs 9.9
c 0
b 0
f 0
1
# Author: ...
2
# Email: ...
3
# License: MIT License
4
5
import numpy as np
6
7
from ..base_optimizer import BaseOptimizer
8
from ...search import Search
9
10
11
class GridSearchOptimizer(BaseOptimizer, Search):
12
    def __init__(
13
        self,
14
        search_space,
15
        step_size=3,
16
        initialize={},
17
    ):
18
        super().__init__(search_space, initialize)
19
        self.initial_position = np.zeros((self.conv.n_dimensions,), dtype=int)
20
        # current position mapped in [|0,search_space_size-1|] (1D)
21
        self.high_dim_pointer = 0
22
        # direction is a generator of our search space (prime with search_space_size)
23
        self.direction = None
24
        # step_size describes how many steps of size direction are jumped at each iteration
25
        self.step_size = step_size
26
27
    def get_direction(self):
28
        """
29
        Aim here is to generate a prime number of the search space size we call our direction.
30
        As direction is prime with the search space size, we know it is
31
        a generator of Z/(search_space_size*Z).
32
        """
33
        n_dims = self.conv.n_dimensions
34
        search_space_size = self.conv.search_space_size
35
36
        # Find prime number near search_space_size ** (1/n_dims)
37
        dim_root = int(np.round(np.power(search_space_size, 1 / n_dims)))
38
        is_prime = False
39
40
        while not is_prime:
41
            if np.gcd(int(search_space_size), int(dim_root)) == 1:
42
                is_prime = True
43
            else:
44
                dim_root += -1
45
46
        return dim_root
47
48
    def grid_move(self):
49
        """
50
        We convert our pointer of Z/(search_space_size * Z) in a position of our search space.
51
        This algorithm uses a bijection from Z/(search_space_size * Z) -> (Z/dim_1*Z)x...x(Z/dim_n*Z).
52
        """
53
        new_pos = []
54
        dim_sizes = self.conv.dim_sizes
55
        pointer = self.high_dim_pointer
56
57
        # The coordinate of our new position for each dimension is
58
        # the quotient of the pointer by the product of remaining dimensions
59
        # Describes a bijection from Z/search_space_size*Z -> (Z/dim_1*Z)x...x(Z/dim_n*Z)
60
        for dim in range(len(dim_sizes) - 1):
61
            new_pos.append(
62
                pointer // np.prod(dim_sizes[dim + 1 :]) % dim_sizes[dim]
63
            )
64
            pointer = pointer % np.prod(dim_sizes[dim + 1 :])
65
        new_pos.append(pointer)
66
        return np.array(new_pos)
67
68
    @BaseOptimizer.track_nth_iter
69
    def iterate(self):
70
        # If this is the first iteration:
71
        # Generate the direction and return initial_position
72
        if self.direction is None:
73
            self.direction = self.get_direction()
74
            return self.initial_position
75
76
        # If this is not the first iteration:
77
        # Update high_dim_pointer by taking a step of size step_size * direction.
78
79
        # Multiple passes are needed in order to observe the entire search space
80
        # depending on the step_size parameter.
81
        _, current_pass = self.nth_iter, self.high_dim_pointer % self.step_size
82
        current_pass_finished = (
83
            (self.nth_iter + 1) * self.step_size // self.conv.search_space_size
84
            > self.nth_iter * self.step_size // self.conv.search_space_size
85
        )
86
        # Begin the next pass if current is finished.
87
        if current_pass_finished:
88
            self.high_dim_pointer = current_pass + 1
89
        else:
90
            # Otherwise update pointer in Z/(search_space_size*Z)
91
            # using the prime step direction and step_size.
92
            self.high_dim_pointer = (
93
                self.high_dim_pointer + self.step_size * self.direction
94
            ) % self.conv.search_space_size
95
96
        # Compute corresponding position in our search space.
97
        return self.grid_move()
98