diff --git a/src/game/genetics/zygosity.hpp b/src/game/genetics/allele.hpp similarity index 94% rename from src/game/genetics/zygosity.hpp rename to src/game/genetics/allele.hpp index c419bf3..3c4e290 100644 --- a/src/game/genetics/zygosity.hpp +++ b/src/game/genetics/allele.hpp @@ -17,8 +17,8 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_ZYGOSITY_HPP -#define ANTKEEPER_ZYGOSITY_HPP +#ifndef ANTKEEPER_ALLELE_HPP +#define ANTKEEPER_ALLELE_HPP namespace dna { @@ -55,4 +55,4 @@ inline constexpr bool heterozygous(T x) noexcept } // namespace dna -#endif // ANTKEEPER_ZYGOSITY_HPP +#endif // ANTKEEPER_ALLELE_HPP diff --git a/src/game/genetics/bit-math.hpp b/src/game/genetics/bit-math.hpp index b9baa91..5efd020 100644 --- a/src/game/genetics/bit-math.hpp +++ b/src/game/genetics/bit-math.hpp @@ -49,7 +49,7 @@ constexpr T bit_extract(T x, T mask) noexcept; * @param x Value to count. * @return Number of set bits in @p x. */ -template +template constexpr int popcount(T x) noexcept; /** @@ -59,18 +59,18 @@ constexpr int popcount(T x) noexcept; * @param y Second value. * @return Hamming distance between @px and @p y. */ -template +template constexpr int hamming_distance(T x, T y) noexcept; -template +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 +constexpr T bit_expand(T x) noexcept { x &= (1 << (sizeof(T) << 2)) - 1; @@ -81,25 +81,64 @@ constexpr T bit_pad(T x) noexcept if constexpr(sizeof(T) >= 2) x = (x ^ (x << 4)) & T(0x0f0f0f0f0f0f0f0f); - x = (x ^ (x << 2)) & T(0x3333333333333333); - x = (x ^ (x << 1)) & T(0x5555555555555555); + x = (x ^ (x << 2)) & T(0x3333333333333333); + x = (x ^ (x << 1)) & T(0x5555555555555555); return x; } -template -constexpr T bit_interleave(T a, T b) noexcept +template +constexpr U interleave(T a, T b) noexcept { - return (bit_pad(b) << 1) | bit_pad(a); + return bit_expand(a) | (bit_expand(b) << 1); } -template +/** + * 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 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 deinterleave(T x) noexcept +{ + x &= T(0x5555555555555555); + + x = (x ^ (x >> 1)) & T(0x3333333333333333); + x = (x ^ (x >> 2)) & T(0x0f0f0f0f0f0f0f0f); + + if constexpr(sizeof(T) >= 2) + x = (x ^ (x >> 4)) & T(0x00ff00ff00ff00ff); + if constexpr(sizeof(T) >= 4) + x = (x ^ (x >> 8)) & T(0x0000ffff0000ffff); + if constexpr(sizeof(T) >= 8) + x = (x ^ (x >> 16)) & T(0x00000000ffffffff); + + return x; +} + +template constexpr T bit_swap_adjacent(T x) noexcept { return ((x & T(0xaaaaaaaaaaaaaaaa)) >> 1) | ((x & T(0x5555555555555555)) << 1); } -template +template constexpr T bit_shuffle_adjacent(T x, T mask) noexcept { T y = bit_swap_adjacent(x); @@ -169,7 +208,7 @@ constexpr T bit_extract(T x, T mask) noexcept return result; } -template +template constexpr int popcount(T x) noexcept { int n = 0; @@ -178,24 +217,12 @@ constexpr int popcount(T x) noexcept return n; } -template +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/chromosome.hpp b/src/game/genetics/chromosome.hpp new file mode 100644 index 0000000..fd6f924 --- /dev/null +++ b/src/game/genetics/chromosome.hpp @@ -0,0 +1,169 @@ +/* + * 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 "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 = deinterleave(x); + T even = deinterleave(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 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); +} + +/** + * Performs + */ +template +std::array meiosis(T x, T mask) +{ + x = segregate(x); + T xl = x & (sizeof(T) << 2); + T xh = x >> (sizeof(T) << 2); + + T a = xl; + T b = crossover_n(xl, xh, mask); + T c = crossover_n(xh, xl, mask); + T d = xh; + + return {a, b, c, d}; +} + +/** + * + * + * @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 deleted file mode 100644 index 94a5aee..0000000 --- a/src/game/genetics/crossover.hpp +++ /dev/null @@ -1,74 +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_CROSSOVER_HPP -#define ANTKEEPER_CROSSOVER_HPP - -#include "bit-math.hpp" - -namespace dna -{ - -/** - * 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 First value. - * @param b Second value. - * @param mask Bit mask with set bits marking crossover points. - */ -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 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; - - while (mask) - { - merge ^= (mask ^ (mask - 1)) >> 1; - mask &= mask - 1; - i = !i; - } - - merge ^= ~T(0) * i; - - return bit_merge(a, b, merge); -} - -} // namespace dna - -#endif // ANTKEEPER_CROSSOVER_HPP diff --git a/src/game/genetics/gene.hpp b/src/game/genetics/gene.hpp new file mode 100644 index 0000000..2fc13d9 --- /dev/null +++ b/src/game/genetics/gene.hpp @@ -0,0 +1,56 @@ +/* + * 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/trait.hpp similarity index 72% rename from src/game/genetics/mutate.hpp rename to src/game/genetics/trait.hpp index 78fe93c..5d019ff 100644 --- a/src/game/genetics/mutate.hpp +++ b/src/game/genetics/trait.hpp @@ -17,25 +17,24 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_MUTATE_HPP -#define ANTKEEPER_MUTATE_HPP +#ifndef ANTKEEPER_TRAIT_HPP +#define ANTKEEPER_TRAIT_HPP namespace dna { -/** - * 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 +T mendelian_variant(T x) +{ + return !!x; +} + +template +T nonmendelian_variant(T x) { - return x ^ (T(1) << i); + return popcount(x); } } // namespace dna -#endif // ANTKEEPER_MUTATE_HPP +#endif // ANTKEEPER_TRAIT_HPP