Skip to main content

radiate_gp/collections/graphs/
chromosome.rs

1use crate::{Factory, GraphNode, NodeStore, node::Node};
2use radiate_core::{Chromosome, Gene, Valid};
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5use std::{fmt::Debug, hash::Hash};
6
7/// A chromosome type that represents a directed graph structure for genetic programming.
8/// This chromosome is essentially just a graph, the only difference is the name of the struct.
9/// The graph and the [GraphChromosome] are interchangeable, the only difference is who holds
10/// the vector of nodes. For instance, to create a graph from the [GraphChromosome], just take
11/// the vector of nodes and give them to a new graph instance - boom, you have a graph.
12///
13/// [GraphChromosome] is a specialized chromosome type that maintains a collection of graph nodes
14/// and their connections. It's designed for genetic programming applications where the solution
15/// space can be represented as a directed graph.
16///
17/// # Type Parameters
18/// * `T` - The type of value stored in each node. Must implement `Clone` and `PartialEq`.
19///
20/// # Structure
21/// The chromosome consists of:
22/// * A vector of [`GraphNode<T>`] instances representing the graph structure
23/// * An optional [`NodeStore<T>`] for managing node creation and validation. This makes
24///   the creation of new nodes easier and more uniform across the genetic algorithm.
25///
26/// # Features
27/// * Maintains graph connectivity through node connections
28/// * Provides factory methods for creating new instances
29/// * Implements serialization when the "serde" feature is enabled
30/// * Allows the graph nodes, essentially the graph, to be evolved through the genetic algorithm
31///
32/// # Examples
33/// ```
34/// use radiate_gp::collections::graphs::{GraphChromosome, GraphNode, Graph};
35/// use radiate_gp::{NodeStore, node_store, NodeType};
36///
37/// // Create a new chromosome with some nodes
38///let store = node_store! {
39///     Input => vec![1, 2, 3],
40///     Output => vec![4, 5, 6],
41///     Edge => vec![7, 8, 9],
42///     Vertex => vec![10, 11, 12]
43/// };
44///
45/// let graph = Graph::directed(1, 1, store.clone());
46///
47/// let chromosome = GraphChromosome::from((graph, store));
48/// ```
49///
50/// # Genetic Operations
51/// The chromosome supports several genetic operations:
52/// * Crossover through `GraphCrossover`
53/// * Mutation through `GraphMutator`
54/// * Replacement through `GraphReplacement`
55///
56/// # Serialization
57/// When the "serde" feature is enabled, the chromosome can be serialized and deserialized.
58/// The serialization preserves the graph structure and node values, but not the node store.
59///
60/// # Performance
61/// * Node access is O(1) through vector indexing
62/// * Graph operations (adding/removing nodes/edges) are O(log n) due to BTreeSet usage
63/// * Memory usage is O(V + E) where V is the number of nodes and E is the number of edges
64#[derive(Clone)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66pub struct GraphChromosome<T> {
67    nodes: Vec<GraphNode<T>>,
68    store: Option<NodeStore<T>>,
69    max_nodes: Option<usize>,
70}
71
72impl<T> GraphChromosome<T> {
73    pub fn new(nodes: Vec<GraphNode<T>>, factory: NodeStore<T>) -> Self {
74        GraphChromosome {
75            nodes,
76            store: Some(factory),
77            max_nodes: None,
78        }
79    }
80
81    pub fn with_max_nodes(mut self, max_nodes: usize) -> Self {
82        self.max_nodes = Some(max_nodes + self.nodes.len());
83        self
84    }
85
86    pub fn take_nodes(&mut self) -> Vec<GraphNode<T>> {
87        std::mem::take(&mut self.nodes)
88    }
89
90    pub fn set_nodes(&mut self, nodes: Vec<GraphNode<T>>) {
91        self.nodes = nodes;
92    }
93
94    pub fn store(&self) -> Option<&NodeStore<T>> {
95        self.store.as_ref()
96    }
97
98    pub fn max_nodes(&self) -> Option<usize> {
99        self.max_nodes
100    }
101}
102
103impl<T> Factory<Option<NodeStore<T>>, GraphChromosome<T>> for GraphChromosome<T>
104where
105    T: Clone + PartialEq + Default,
106{
107    fn new_instance(&self, input: Option<NodeStore<T>>) -> GraphChromosome<T> {
108        input
109            .or_else(|| self.store.clone())
110            .map(|store| GraphChromosome {
111                nodes: self
112                    .iter()
113                    .enumerate()
114                    .filter_map(|(index, node)| {
115                        store
116                            .new_instance((index, node.node_type()))
117                            .map(|new_node| {
118                                let mut new_node = if new_node.arity() == node.arity() {
119                                    node.with_allele(new_node.allele())
120                                } else {
121                                    node.clone()
122                                };
123
124                                new_node.set_innovation(node.innovation());
125                                new_node
126                            })
127                    })
128                    .collect(),
129                store: Some(store),
130                max_nodes: self.max_nodes,
131            })
132            .map(|chromosome| {
133                if chromosome.len() != self.len() {
134                    self.clone()
135                } else {
136                    chromosome
137                }
138            })
139            .unwrap_or_else(|| self.clone())
140    }
141}
142
143impl<T> Chromosome for GraphChromosome<T>
144where
145    T: Clone + PartialEq,
146{
147    type Gene = GraphNode<T>;
148
149    fn get(&self, index: usize) -> Option<&Self::Gene> {
150        self.nodes.get(index)
151    }
152
153    fn get_mut(&mut self, index: usize) -> Option<&mut Self::Gene> {
154        self.nodes.get_mut(index)
155    }
156
157    fn set(&mut self, index: usize, gene: Self::Gene) {
158        if let Some(slot) = self.nodes.get_mut(index) {
159            *slot = gene;
160        }
161    }
162
163    fn iter(&self) -> impl Iterator<Item = &Self::Gene> {
164        self.nodes.iter()
165    }
166
167    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Gene> {
168        self.nodes.iter_mut()
169    }
170
171    fn len(&self) -> usize {
172        self.nodes.len()
173    }
174}
175
176impl<T> Valid for GraphChromosome<T> {
177    #[inline]
178    fn is_valid(&self) -> bool {
179        self.nodes.iter().all(|gene| gene.is_valid())
180    }
181}
182
183impl<T> AsRef<[GraphNode<T>]> for GraphChromosome<T> {
184    fn as_ref(&self) -> &[GraphNode<T>] {
185        &self.nodes
186    }
187}
188
189impl<T> AsMut<[GraphNode<T>]> for GraphChromosome<T> {
190    fn as_mut(&mut self) -> &mut [GraphNode<T>] {
191        &mut self.nodes
192    }
193}
194
195impl<T: PartialEq> PartialEq for GraphChromosome<T> {
196    fn eq(&self, other: &Self) -> bool {
197        self.nodes == other.nodes
198    }
199}
200
201impl<T: Hash> Hash for GraphChromosome<T> {
202    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
203        for node in self.as_ref() {
204            node.hash(state);
205        }
206    }
207}
208
209impl<T> From<Vec<GraphNode<T>>> for GraphChromosome<T> {
210    fn from(nodes: Vec<GraphNode<T>>) -> Self {
211        GraphChromosome {
212            nodes,
213            store: None,
214            max_nodes: None,
215        }
216    }
217}
218
219impl<T, I> From<(I, NodeStore<T>)> for GraphChromosome<T>
220where
221    I: IntoIterator<Item = GraphNode<T>>,
222{
223    fn from((iter, store): (I, NodeStore<T>)) -> Self {
224        GraphChromosome {
225            nodes: iter.into_iter().collect(),
226            store: Some(store),
227            max_nodes: None,
228        }
229    }
230}
231
232impl<T> FromIterator<GraphNode<T>> for GraphChromosome<T> {
233    fn from_iter<I: IntoIterator<Item = GraphNode<T>>>(iter: I) -> Self {
234        GraphChromosome {
235            nodes: iter.into_iter().collect(),
236            store: None,
237            max_nodes: None,
238        }
239    }
240}
241
242impl<T> IntoIterator for GraphChromosome<T> {
243    type Item = GraphNode<T>;
244    type IntoIter = std::vec::IntoIter<GraphNode<T>>;
245
246    fn into_iter(self) -> Self::IntoIter {
247        self.nodes.into_iter()
248    }
249}
250
251impl<T: Debug> Debug for GraphChromosome<T> {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        writeln!(f, "Graph {{")?;
254        for node in self.as_ref() {
255            writeln!(f, "  {:?},", node)?;
256        }
257        write!(f, "}}")
258    }
259}