diff --git a/src/game/genetics/bit-math.hpp b/src/game/genetics/bit-math.hpp index e2b0e9a..b9baa91 100644 --- a/src/game/genetics/bit-math.hpp +++ b/src/game/genetics/bit-math.hpp @@ -52,6 +52,93 @@ constexpr T bit_extract(T x, T mask) noexcept; template constexpr int popcount(T x) 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 hamming_distance(T x, T y) noexcept; + + +template +constexpr T bit_merge(T a, T b, T mask) noexcept +{ + return a ^ ((a ^ b) & mask); +} + +template +constexpr T bit_pad(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 +constexpr T bit_interleave(T a, T b) noexcept +{ + return (bit_pad(b) << 1) | bit_pad(a); +} + +template +constexpr T bit_swap_adjacent(T x) noexcept +{ + return ((x & T(0xaaaaaaaaaaaaaaaa)) >> 1) | ((x & T(0x5555555555555555)) << 1); +} + +template +constexpr T bit_shuffle_adjacent(T x, T mask) noexcept +{ + T y = bit_swap_adjacent(x); + return bit_merge(x, y, bit_interleave(mask, mask)); +} + +/** + * Shuffles the adjacent bits of a value. + * + * @param x Value to shuffle. + * @param g Uniform random bit generator. + * @return Value with adjacent bits shuffled. + */ +/* +template +constexpr T bit_shuffle_adjacent(T x, URBG&& g) noexcept +{ + return bit_swap_adjacent(x, static_cast(g())); +}*/ + +/** + * + */ +template +U bit_splice(T a, T b, U mask) +{ + return bit_deposit(static_cast(a), ~mask) | bit_deposit(static_cast(b), mask); +} + +/** + * For an n-bit number with r set bits, there are `n! / ((n - r)! * r!)` permutations. + */ +template +T next_bit_permutation(T x) +{ + T y = (x | (x - 1)) + 1; + return y | ((((y & -y) / (x & -x)) >> 1) - 1); +} + template constexpr T bit_deposit(T x, T mask) noexcept { @@ -91,6 +178,24 @@ constexpr int popcount(T x) noexcept return n; } +template +inline constexpr int hamming_distance(T x, T y) noexcept +{ + return popcount(x ^ y); +} + +/** + * + * @param mask Least significant bits indicate whether to inherit the first (0) or second (1) allele from @p a. Most significant bits indicate whether to inherit the first (0) or second (1) allele from @p b. + */ +template +constexpr T inherit(T a, T b, T mask) noexcept +{ + a = bit_shuffle_adjacent(a, mask); + b = bit_shuffle_adjacent(b, mask >> (sizeof(T) << 2)); + return bit_merge(a, b, T(0x5555555555555555)); +} + } // namespace dna #endif // ANTKEEPER_BIT_MATH_HPP diff --git a/src/game/genetics/crossover.hpp b/src/game/genetics/crossover.hpp index 51cb4ae..94a5aee 100644 --- a/src/game/genetics/crossover.hpp +++ b/src/game/genetics/crossover.hpp @@ -20,32 +20,53 @@ #ifndef ANTKEEPER_CROSSOVER_HPP #define ANTKEEPER_CROSSOVER_HPP +#include "bit-math.hpp" + namespace dna { /** - * Performs a genetic crossover between two chromosomes. + * Performs a single-point crossover between two values. + * + * @param a First value. + * @param b Second value. + * @parma pos Position of the crossover point. + */ +template +constexpr T crossover(T a, T b, int pos) noexcept; + +/** + * Performs an n-point crossover between two values. * - * @param a Chromosome of the first parent. - * @param b Chromosome of the second parent. - * @param g Uniform random bit generator. - * @return Chromosome of the new offspring. + * @param a First value. + * @param b Second value. + * @param mask Bit mask with set bits marking crossover points. */ -template -T crossover(T a, T b, URBG&& g) +template +constexpr T crossover_n(T a, T b, T mask) noexcept; + +template +inline constexpr T crossover(T a, T b, int pos) noexcept { - T c = 0, i = 1; + T mask = (T(1) << pos) - 1; + return bit_merge(b, a, mask); +} + +template +constexpr T crossover_n(T a, T b, T mask) noexcept +{ + T merge = 0, i = 0; - do + while (mask) { - c |= (a >> (g() % 2)) & i; - i <<= 1; - c |= (b << (g() % 2)) & i; - i <<= 1; + merge ^= (mask ^ (mask - 1)) >> 1; + mask &= mask - 1; + i = !i; } - while (i); - return c; + merge ^= ~T(0) * i; + + return bit_merge(a, b, merge); } } // namespace dna diff --git a/src/game/genetics/mutate.hpp b/src/game/genetics/mutate.hpp index 638b4d5..78fe93c 100644 --- a/src/game/genetics/mutate.hpp +++ b/src/game/genetics/mutate.hpp @@ -24,16 +24,16 @@ namespace dna { /** - * Mutates a value by flipping a single random bit. + * Mutates a value by flipping a single bit. * * @param x Value to mutate. - * @param g Uniform random bit generator. + * @param i Index of the bit to flip. * @return Mutated copy of @p x. */ -template -T mutate(T x, URBG&& g) +template +T mutate(T x, int i) { - return x ^ (T(1) << (g() % (sizeof(T) << 3))); + return x ^ (T(1) << i); } } // namespace dna diff --git a/src/game/genetics/zygosity.hpp b/src/game/genetics/zygosity.hpp index ad1cd1e..c419bf3 100644 --- a/src/game/genetics/zygosity.hpp +++ b/src/game/genetics/zygosity.hpp @@ -24,33 +24,33 @@ namespace dna { /** - * Tests a gene for heterozygosity. + * Tests the two least significant bits of a value for equality * - * @param x Gene with alleles encoded in the two least significant bits. - * @return `true` if the gene is heterozygous, `false` otherwise. + * @param x Value to test. + * @return `true` if the two least significant bits are equal, `false` otherwise. */ template -bool is_heterozygous(T x); +constexpr bool homozygous(T x) noexcept; /** - * Tests a gene for homozygosity. + * Tests the two least significant bits of a value for inequality. * - * @param x Gene with alleles encoded in the two least significant bits. - * @return `true` if the gene is homozygous, `false` otherwise. + * @param x Value to test. + * @return `true` if the two least significant bits are inequal, `false` otherwise. */ template -bool is_homozygous(T x); +constexpr bool heterozygous(T x) noexcept; template -inline bool is_heterozygous(T x) +inline constexpr bool homozygous(T x) noexcept { - return (x & 1) != (x >> 1); + return (x & 1) == ((x >> 1) & 1); } template -inline bool is_homozygous(T x) +inline constexpr bool heterozygous(T x) noexcept { - return (x & 1) == (x >> 1); + return (x & 1) != ((x >> 1) & 1); } } // namespace dna