rlevo_evolution/genome.rs
1//! Genome category trait and its zero-sized marker types.
2//!
3//! [`GenomeKind`] tags genome representations at the type level so operators
4//! can specialize on the element semantics (real-valued, binary, integer,
5//! or tree). Strategies take a marker type as a const generic to pick the
6//! right operator set.
7//!
8//! The markers themselves carry no data — they exist purely to discriminate
9//! trait impls.
10
11use std::fmt::Debug;
12
13/// Shape-erased genome kind.
14///
15/// `GenomeKind` is a zero-sized marker that strategies parameterize on to
16/// pick operators. Concrete kinds (`Real`, `Binary`, `Integer`, `Tree`,
17/// `Permutation`) live below; new kinds can be added by implementing this
18/// trait on a fresh marker type.
19///
20/// The associated constant [`DIM`](GenomeKind::DIM) records the genome
21/// dimensionality at the type level when it is compile-time known (for
22/// variable-length representations like trees, impls set it to `0`).
23pub trait GenomeKind: Debug + Copy + Send + Sync + 'static {
24 /// Compile-time genome dimensionality, or `0` for variable-length kinds.
25 const DIM: usize;
26
27 /// Element type of the genome (typically `f32`, `i32`, or `bool`).
28 type Element: Copy + Debug + Send + Sync + 'static;
29}
30
31/// Real-valued genome (each gene is an `f32`).
32///
33/// Populations are stored as `Tensor<B, 2>` of shape `(pop_size, dim)`.
34/// All classical ES variants, real-coded GA, EP, and DE use this kind.
35#[derive(Debug, Clone, Copy, Default)]
36pub struct Real;
37
38impl GenomeKind for Real {
39 const DIM: usize = 0;
40 type Element = f32;
41}
42
43/// Binary genome (each gene is a bit, stored as `i32` 0/1 on device).
44///
45/// Populations are stored as `Tensor<B, 2, Int>` of shape
46/// `(pop_size, dim)`. Binary-coded GA uses this kind.
47#[derive(Debug, Clone, Copy, Default)]
48pub struct Binary;
49
50impl GenomeKind for Binary {
51 const DIM: usize = 0;
52 type Element = i32;
53}
54
55/// Integer-valued genome (each gene is a non-negative integer index).
56///
57/// Populations are stored as `Tensor<B, 2, Int>` of shape
58/// `(pop_size, dim)`. Permutation-coded GA and Cartesian GP use this kind.
59#[derive(Debug, Clone, Copy, Default)]
60pub struct Integer;
61
62/// Tree-based genome (variable-length AST, stored host-side).
63///
64/// Reserved for classical Koza-style GP in a future release. Tree
65/// genomes cannot be batched on a GPU and therefore have no tensor
66/// representation in this crate.
67#[derive(Debug, Clone, Copy, Default)]
68pub struct Tree;
69
70/// Permutation genome (each row is a permutation of `0..n_nodes`).
71///
72/// Populations are stored as `Tensor<B, 2, Int>` of shape
73/// `(pop_size, n_nodes)` where every row is a valid permutation. Used by
74/// Ant Colony Optimization over combinatorial domains (TSP, QAP, …);
75/// only a stubbed consumer ships in this release — a full implementation
76/// is planned for a future release.
77#[derive(Debug, Clone, Copy, Default)]
78pub struct Permutation;
79
80impl GenomeKind for Integer {
81 const DIM: usize = 0;
82 type Element = i32;
83}
84
85impl GenomeKind for Tree {
86 const DIM: usize = 0;
87 type Element = i32;
88}
89
90impl GenomeKind for Permutation {
91 const DIM: usize = 0;
92 type Element = i32;
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn real_has_f32_element() {
101 let _: <Real as GenomeKind>::Element = 0.0_f32;
102 }
103
104 #[test]
105 fn binary_has_i32_element() {
106 let _: <Binary as GenomeKind>::Element = 1_i32;
107 }
108
109 #[test]
110 fn integer_has_i32_element() {
111 let _: <Integer as GenomeKind>::Element = 5_i32;
112 }
113
114 #[test]
115 fn permutation_has_i32_element() {
116 let _: <Permutation as GenomeKind>::Element = 7_i32;
117 }
118
119 #[test]
120 fn markers_are_debug() {
121 let _ = format!(
122 "{Real:?} {Binary:?} {Integer:?} {Tree:?} {Permutation:?}"
123 );
124 }
125}