Skip to content

Utils

Encoding

Bases: ABC

Source code in metazoo/src/metazoo/bio/utils.py
 7
 8
 9
10
11
12
13
14
class Encoding(ABC):
    @abstractmethod
    def encode(self, population_size: int) -> np.ndarray:
        pass

    @abstractmethod
    def decode(self, individual: np.ndarray) -> np.ndarray:
        pass

Binary

Bases: Encoding

Solves real-valued problems using binary encoding. Each variable is represented by a fixed number of bits, determined by the desired precision and the variable bounds.

Source code in metazoo/src/metazoo/bio/utils.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class Binary(Encoding):
    """
    Solves real-valued problems using binary encoding.
    Each variable is represented by a fixed number of bits, determined by the desired precision and the variable bounds.
    """

    def __init__(
        self, precision: int = 3, bounds: Sequence[Tuple[float, float]] = None
    ):
        self.precision = precision
        self.bounds: Sequence[Tuple[float, float]] = bounds
        if bounds is None:
            raise ValueError("Bounds must be provided for binary encoding.")
        self.dim = len(bounds)  # Number of variables

        self.epsilon = 10 ** (
            -precision
        )  # ε = Obtaianable accuracy for binary encoding

        # Bits per variable for binary encoding
        # This is calculated based on the precision and the range of each variable
        # Using the formula: n = ceil(log2((XU - XL) / ε))
        # where XU and XL are the upper and lower bounds of the variable, and ε is the desired precision
        # We can understand ε as the smallest difference we want to be able to represent between two values of the variable
        self.bits_per_var = max(
            int(np.ceil(np.log2((xu - xl) / self.epsilon))) for xl, xu in self.bounds
        )
        # We use max to ensure we have enough bits for the most constrained variable

        self.genome_length = self.bits_per_var * self.dim

    def decode(self, individual: np.ndarray) -> np.ndarray:
        """
        Decodes a binary individual into its real-valued representation.
        """
        bits_per_var = self.bits_per_var
        decoded = []
        for i, (a, b) in enumerate(self.bounds):
            bits = individual[i * bits_per_var : (i + 1) * bits_per_var]
            value = int("".join(str(int(bit)) for bit in bits), 2)
            max_value = 2**bits_per_var - 1
            real_value = a + (b - a) * value / max_value
            decoded.append(real_value)
        return np.array(decoded)

    def encode(self, population_size: int) -> np.ndarray:
        return np.random.randint(0, 2, size=(population_size, self.genome_length))

decode(individual)

Decodes a binary individual into its real-valued representation.

Source code in metazoo/src/metazoo/bio/utils.py
48
49
50
51
52
53
54
55
56
57
58
59
60
def decode(self, individual: np.ndarray) -> np.ndarray:
    """
    Decodes a binary individual into its real-valued representation.
    """
    bits_per_var = self.bits_per_var
    decoded = []
    for i, (a, b) in enumerate(self.bounds):
        bits = individual[i * bits_per_var : (i + 1) * bits_per_var]
        value = int("".join(str(int(bit)) for bit in bits), 2)
        max_value = 2**bits_per_var - 1
        real_value = a + (b - a) * value / max_value
        decoded.append(real_value)
    return np.array(decoded)

Real

Bases: Encoding

Solves real-valued problems using real encoding. Each variable is represented directly by a real number within the specified bounds.

Source code in metazoo/src/metazoo/bio/utils.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class Real(Encoding):
    """
    Solves real-valued problems using real encoding.
    Each variable is represented directly by a real number within the specified bounds.
    """

    def __init__(self, bounds: Sequence[Tuple[float, float]] = None):
        self.bounds: Sequence[Tuple[float, float]] = bounds
        if bounds is None:
            raise ValueError("Bounds must be provided for real encoding.")
        self.dim = len(bounds)  # Number of variables
        self.genome_length = self.dim

    def decode(self, individual: np.ndarray) -> np.ndarray:
        return individual  # For real encoding, the individual is already in real values

    def encode(self, population_size: int):
        arr = np.empty((population_size, self.genome_length))
        for j, (low, high) in enumerate(self.bounds):
            arr[:, j] = np.random.uniform(low, high, size=population_size)
        return arr

Permutation

Bases: Encoding

Solves combinatorial problems using permutation encoding. Each individual is represented as a permutation of integers from 0 to n-1, where n is the size of the permutation.

Source code in metazoo/src/metazoo/bio/utils.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
class Permutation(Encoding):
    """
    Solves combinatorial problems using permutation encoding.
    Each individual is represented as a permutation of integers from 0 to n-1, where n is the size of the permutation.
    """

    def __init__(self, permutation_size: int):
        self.genome_length = permutation_size

    def decode(self, individual: np.ndarray) -> np.ndarray:
        return individual  # For permutation encoding, the individual is already in permutation form

    def encode(self, population_size: int) -> np.ndarray:
        return np.array(
            [np.random.permutation(self.genome_length) for _ in range(population_size)]
        )

Population

Source code in metazoo/src/metazoo/bio/utils.py
113
114
115
116
117
class Population:
    def __init__(self, population_size: int, encoding: Encoding):
        self.size = population_size
        self.encoding = encoding
        self.individuals = encoding.encode(population_size)