diff --git a/src/game/genetics/allele.hpp b/src/game/genetics/allele.hpp deleted file mode 100644 index 3c4e290..0000000 --- a/src/game/genetics/allele.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2020 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ALLELE_HPP -#define ANTKEEPER_ALLELE_HPP - -namespace dna -{ - -/** - * Tests the two least significant bits of a value for equality - * - * @param x Value to test. - * @return `true` if the two least significant bits are equal, `false` otherwise. - */ -template -constexpr bool homozygous(T x) noexcept; - -/** - * Tests the two least significant bits of a value for inequality. - * - * @param x Value to test. - * @return `true` if the two least significant bits are inequal, `false` otherwise. - */ -template -constexpr bool heterozygous(T x) noexcept; - -template -inline constexpr bool homozygous(T x) noexcept -{ - return (x & 1) == ((x >> 1) & 1); -} - -template -inline constexpr bool heterozygous(T x) noexcept -{ - return (x & 1) != ((x >> 1) & 1); -} - -} // namespace dna - -#endif // ANTKEEPER_ALLELE_HPP diff --git a/src/game/genetics/chromosome.hpp b/src/game/genetics/chromosome.hpp deleted file mode 100644 index cd2afb8..0000000 --- a/src/game/genetics/chromosome.hpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2020 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_CHROMOSOME_HPP -#define ANTKEEPER_CHROMOSOME_HPP - -#include "utility/bit-math.hpp" -#include - -namespace dna -{ - -/** - * Segregates the odd and even bits of a value. - * - * @param x Value to segregate. - * @return Value with even bits of @p x in the lower half, and odd bits in the upper half. - */ -template -T segregate(T x) -{ - T odd = bit::compress(x); - T even = bit::compress(x >> 1); - - return odd | (even << (sizeof(T) << 2)); -} - -/** - * Interleaves bits of the lower and upper halves of a value. - * - * @param x Value to desegregate. - * @return Value with bits from the upper half of @p x interleaved with bits from the lower half. - */ -template -T desegregate(T x) -{ - return bit::interleave(x, x >> (sizeof(T) << 2)); -} - -/** - * Replicates each bit in the lower half of a value. - * - * @param x Value to replicate. - * @return Value of @p x interleaved with itself. - */ -template -T replicate(T x) -{ - x = bit::expand(x); - return x | (x << 1); -} - -/** - * Generates four daughter chromosomes from a 2n/2c chromosome pair. - * - * @param x Homologous pair of chromosomes. - * @param mask Bit mask with set bits marking crossover points. - * @return Array of four 1n, 1c chromosomes. - */ -template -std::array meiosis(T x, U mask) -{ - U a = bit::compress(x); - U b = bit::compress(x >> 1); - U c = crossover_n(a, b, mask); - U d = crossover_n(b, a, mask); - - return {a, c, d, b}; -} - -/** - * - * - * @param a 2c chromosome of first parent. - * @param b 2c chromosome of second parent. - * @param g Uniform random bit generator. `g()` will be called three times. - * @return 2c chromosome of child. - */ -template -T reproduce(T a, T b, URBG&& g) -{ - auto gametes_a = meiosis(a, static_cast(g())); - auto gametes_b = meiosis(b, static_cast(g())); - - T i = static_cast(g()); - T ca = gametes_a[i & 3]; - T cb = gametes_b[(i >> 2) & 3]; - - return interleave(ca, cb); -} - -/** - * Performs a single-point crossover between two values. - * - * @param a First value. - * @param b Second value. - * @param i Index of the crossover point. - * @return Crossed over value. - */ -template -constexpr T crossover(T a, T b, int i) noexcept; - -/** - * Performs an n-point crossover between two values. - * - * @param a First value. - * @param b Second value. - * @param mask Bit mask with set bits marking crossover points. - * @return Crossed over value. - */ -template -constexpr T crossover_n(T a, T b, T mask) noexcept; - -/** - * Mutates a value by flipping a single bit. - * - * @param x Value to mutate. - * @param i Index of the bit to flip. - * @return Mutated copy of @p x. - */ -template -T mutate(T x, int i); - -template -inline constexpr T crossover(T a, T b, int i) noexcept -{ - T mask = (T(1) << i) - 1; - return bit_merge(b, a, mask); -} - -template -constexpr T crossover_n(T a, T b, T mask) noexcept -{ - T merge = ~T(0) * parity(mask); - - while (mask) - { - merge ^= (mask ^ (mask - 1)) >> 1; - mask &= mask - 1; - } - - return bit_merge(a, b, merge); -} - -template -inline T mutate(T x, int i) -{ - return x ^ (T(1) << i); -} - -} // namespace dna - -#endif // ANTKEEPER_CHROMOSOME_HPP diff --git a/src/game/genetics/crossover.hpp b/src/game/genetics/crossover.hpp new file mode 100644 index 0000000..4cae2ed --- /dev/null +++ b/src/game/genetics/crossover.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_CROSSOVER_HPP +#define ANTKEEPER_CROSSOVER_HPP + +#include +#include +#include + +namespace dna +{ + +/** + * Exchanges elements between two ranges, starting at a random offset. + * + * @param first1,last1 First range of elements to crossover. + * @param first2 Beginning of the second range of elements to crossover. + * @param g Uniform random bit generator. + * @return Iterator to the start of the crossover in the second range. + */ +template +ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g); + +/** + * Exchanges elements between two ranges multiple times, starting at a random offset each time. + * + * @param first1,last1 First range of elements to crossover. + * @param first2 Beginning of the second range of elements to crossover. + * @param count Number of times to crossover. + * @param g Uniform random bit generator. + */ +template +void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g); + +template +ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); + difference_t pos = distribution(g); + std::advance(first1, pos); + std::advance(first2, pos); + std::swap_ranges(first1, last1, first2); + return first2; +} + +template +void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); + ForwardIt1 crossover1, crossover2; + + while (count) + { + crossover1 = first1; + crossover2 = first2; + + difference_t pos = distribution(g); + std::advance(crossover1, pos); + std::advance(crossover2, pos); + std::swap_ranges(crossover1, last1, crossover2); + + --count; + } +} + +} // namespace dna + +#endif // ANTKEEPER_CROSSOVER_HPP diff --git a/src/game/genetics/gene.hpp b/src/game/genetics/gene.hpp deleted file mode 100644 index 2fc13d9..0000000 --- a/src/game/genetics/gene.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2020 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GENE_HPP -#define ANTKEEPER_GENE_HPP - -namespace dna -{ - -/** - * Isolates a pair of bits and returns them in the two least significant bits. - * - * @param x Diploid chromosome with interleaved alleles. - * @param i Index of the pair to isolate. - * @return Isolated pair of bits in the two least significant bits. - */ -template -T isolate(T x, int i) -{ - return (x >> (i << 1)) & 0b11; -} - -/** - * Isolates a sequence of genes and returns their alleles in the least significant bits. - * - * @param x Diploid chromosome with interleaed alleles. - * @param i Index of the first gene in the sequence. - * @param n Length of the sequence, in genes. - * @return Alleles of the sequeuence in the least significant bits. - */ -template -T isolate_n(T x, int i, int n) -{ - T mask = (T(1) << (n << 1)) - 1; - return (x >> (i << 1)) & mask; -} - -} // namespace dna - -#endif // ANTKEEPER_GENE_HPP diff --git a/src/game/genetics/mutate.hpp b/src/game/genetics/mutate.hpp new file mode 100644 index 0000000..d4dc2d2 --- /dev/null +++ b/src/game/genetics/mutate.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MUTATE_HPP +#define ANTKEEPER_MUTATE_HPP + +#include +#include +#include + +namespace dna +{ + +/** + * Applies the given function to a randomly selected element in a range. + * + * @param first,last Range of elements to mutate. + * @param unary_op Unary operation function object that will be applied. + * @param g Uniform random bit generator. + * @return Iterator to the mutated element. + */ +template +ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g); + +/** + * Applies the given function to a random selection of elements in a range. + * + * @param first,last Range of elements to mutate. + * @param count Number of elements to mutate. + * @param unary_op Unary operation function object that will be applied. + * @param g Uniform random bit generator. + */ +template +void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g); + +template +ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); + std::advance(first, distribution(g)); + *first = unary_op(*first); + + return first; +} + +template +void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); + ForwardIt mutation; + + while (count) + { + mutation = first; + std::advance(mutation, distribution(g)); + *mutation = unary_op(*mutation); + --count; + } +} + +} // namespace dna + +#endif // ANTKEEPER_MUTATE_HPP diff --git a/src/utility/bit-math.hpp b/src/utility/bit-math.hpp index 775a708..a42d8de 100644 --- a/src/utility/bit-math.hpp +++ b/src/utility/bit-math.hpp @@ -23,6 +23,43 @@ namespace bit { +/** + * + */ +template +constexpr T compress(T x) noexcept; + +/** + * Returns the number of set bits in a value, known as a *population count* or *Hamming weight*. + * + * @param x Value to count. + * @return Number of set bits in @p x. + */ +template +constexpr int count(T x) noexcept; + +/** + * Performs a single-point crossover between two values. + * + * @param a First value. + * @param b Second value. + * @param i Index of the crossover point. + * @return Crossed over value. + */ +template +constexpr T crossover(T a, T b, int i) noexcept; + +/** + * Performs an n-point crossover between two values. + * + * @param a First value. + * @param b Second value. + * @param mask Bit mask with set bits marking crossover points. + * @return Crossed over value. + */ +template +constexpr T crossover_n(T a, T b, T mask) noexcept; + /** * Reads bits from the least significant bits of a value and returns them in the positions marked by a mask. * @@ -33,6 +70,16 @@ namespace bit template constexpr T deposit(T x, T mask) noexcept; +/** + * Returns the number of differing bits between two values, known as *Hamming distance*. + * + * @param x First value. + * @param y Second value. + * @return Hamming distance between @px and @p y. + */ +template +constexpr int difference(T x, T y) noexcept; + /** * Reads bits from a value in the positions marked by a mask and returns them in the least significant bits. * @@ -44,75 +91,112 @@ template constexpr T extract(T x, T mask) noexcept; /** - * Returns the number of set bits in a value, known as a *population count* or *Hamming weight*. + * Flips a single bit in a value. * - * @param x Value to count. - * @return Number of set bits in @p x. + * @param x Value to modify. + * @param i Index of the bit to flip. + * @return Copy of @p x with one bit flipped. */ template -constexpr int count(T x) noexcept; +constexpr T flip(T x, int i) noexcept; /** - * Returns the number of differing bits between two values, known as *Hamming distance*. * - * @param x First value. - * @param y Second value. - * @return Hamming distance between @px and @p y. - */ + */ +template +constexpr U interleave(T a, T b) noexcept; + +/** + * + */ template -constexpr int difference(T x, T y) noexcept; +constexpr T merge(T a, T b, T mask) noexcept; +/** + * Returns the parity of a value. + * + * @param x Value to test. + * @return `1` if an odd number of bits are set, `0` otherwise. + */ template -constexpr T merge(T a, T b, T mask) noexcept +constexpr T parity(T x) noexcept; + +/** + * + */ +template +constexpr T swap_adjacent(T x) noexcept; + + +/** + * Segregates the odd and even bits of a value. + * + * @param x Value to segregate. + * @return Value with even bits of @p x in the lower half, and odd bits in the upper half. + */ +template +T segregate(T x) { - return a ^ ((a ^ b) & mask); + T odd = bit::compress(x); + T even = bit::compress(x >> 1); + + return odd | (even << (sizeof(T) << 2)); } +/** + * Interleaves bits of the lower and upper halves of a value. + * + * @param x Value to desegregate. + * @return Value with bits from the upper half of @p x interleaved with bits from the lower half. + */ template -constexpr T expand(T x) noexcept +T desegregate(T x) { - x &= (1 << (sizeof(T) << 2)) - 1; - - if constexpr(sizeof(T) >= 8) - x = (x ^ (x << 16)) & T(0x0000ffff0000ffff); - if constexpr(sizeof(T) >= 4) - x = (x ^ (x << 8)) & T(0x00ff00ff00ff00ff); - if constexpr(sizeof(T) >= 2) - x = (x ^ (x << 4)) & T(0x0f0f0f0f0f0f0f0f); - - x = (x ^ (x << 2)) & T(0x3333333333333333); - x = (x ^ (x << 1)) & T(0x5555555555555555); - - return x; + return bit::interleave(x, x >> (sizeof(T) << 2)); } -template -inline constexpr U interleave(T a, T b) noexcept +/** + * Replicates each bit in the lower half of a value. + * + * @param x Value to replicate. + * @return Value of @p x interleaved with itself. + */ +template +T replicate(T x) { - return bit_expand(a) | (bit_expand(b) << 1); + x = bit::expand(x); + return x | (x << 1); } /** - * Returns the parity of a value. + * Isolates a pair of bits and returns them in the two least significant bits. * - * @param x Value to test. - * @return `1` if an odd number of bits are set, `0` otherwise. + * @param x Diploid chromosome with interleaved alleles. + * @param i Index of the pair to isolate. + * @return Isolated pair of bits in the two least significant bits. */ template -constexpr T parity(T x) noexcept +T isolate(T x, int i) { - if constexpr(sizeof(T) >= 8) - x ^= x >> 32; - if constexpr(sizeof(T) >= 4) - x ^= x >> 16; - if constexpr(sizeof(T) >= 2) - x ^= x >> 8; - - x ^= x >> 4; - - return (0x6996 >> (x & 0xf)) & 1; + return (x >> (i << 1)) & 0b11; +} + +/** + * Isolates a sequence of genes and returns their alleles in the least significant bits. + * + * @param x Diploid chromosome with interleaed alleles. + * @param i Index of the first gene in the sequence. + * @param n Length of the sequence, in genes. + * @return Alleles of the sequeuence in the least significant bits. + */ +template +T isolate_n(T x, int i, int n) +{ + T mask = (T(1) << (n << 1)) - 1; + return (x >> (i << 1)) & mask; } + template constexpr T compress(T x) noexcept { @@ -132,9 +216,63 @@ constexpr T compress(T x) noexcept } template -constexpr T swap_adjacent(T x) noexcept +constexpr int count(T x) noexcept { - return ((x & T(0xaaaaaaaaaaaaaaaa)) >> 1) | ((x & T(0x5555555555555555)) << 1); + int n = 0; + for (; x; ++n) + x &= x - 1; + return n; +} + +template +inline constexpr T crossover(T a, T b, int i) noexcept +{ + T mask = (T(1) << i) - 1; + return bit::merge(b, a, mask); +} + +template +constexpr T crossover_n(T a, T b, T mask) noexcept +{ + T merge = ~T(0) * bit::parity(mask); + + while (mask) + { + merge ^= (mask ^ (mask - 1)) >> 1; + mask &= mask - 1; + } + + return bit::merge(a, b, merge); +} + +template +constexpr T expand(T x) noexcept +{ + x &= (1 << (sizeof(T) << 2)) - 1; + + if constexpr(sizeof(T) >= 8) + x = (x ^ (x << 16)) & T(0x0000ffff0000ffff); + if constexpr(sizeof(T) >= 4) + x = (x ^ (x << 8)) & T(0x00ff00ff00ff00ff); + if constexpr(sizeof(T) >= 2) + x = (x ^ (x << 4)) & T(0x0f0f0f0f0f0f0f0f); + + x = (x ^ (x << 2)) & T(0x3333333333333333); + x = (x ^ (x << 1)) & T(0x5555555555555555); + + return x; +} + +template +inline constexpr T flip(T x, int i) noexcept +{ + return x ^ (T(1) << i); +} + +template +inline constexpr U interleave(T a, T b) noexcept +{ + return expand(a) | (expand(b) << 1); } /** @@ -148,7 +286,7 @@ constexpr T next_permutation(T x) noexcept } template -constexpr T bit_deposit(T x, T mask) noexcept +constexpr T deposit(T x, T mask) noexcept { T result = 0; @@ -178,18 +316,36 @@ constexpr T extract(T x, T mask) noexcept } template -constexpr int count(T x) noexcept +inline constexpr int difference(T x, T y) noexcept { - int n = 0; - for (; x; ++n) - x &= x - 1; - return n; + return count(x ^ y); } template -inline constexpr int difference(T x, T y) noexcept +constexpr T merge(T a, T b, T mask) noexcept { - return count(x ^ y); + return a ^ ((a ^ b) & mask); +} + +template +constexpr T parity(T x) noexcept +{ + if constexpr(sizeof(T) >= 8) + x ^= x >> 32; + if constexpr(sizeof(T) >= 4) + x ^= x >> 16; + if constexpr(sizeof(T) >= 2) + x ^= x >> 8; + + x ^= x >> 4; + + return (0x6996 >> (x & 0xf)) & 1; +} + +template +constexpr T swap_adjacent(T x) noexcept +{ + return ((x & T(0xaaaaaaaaaaaaaaaa)) >> 1) | ((x & T(0x5555555555555555)) << 1); } } // namespace bit