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: Clone> Chromosome for GraphChromosome<T> {
144    type Gene = GraphNode<T>;
145
146    fn get(&self, index: usize) -> Option<&Self::Gene> {
147        self.nodes.get(index)
148    }
149
150    fn get_mut(&mut self, index: usize) -> Option<&mut Self::Gene> {
151        self.nodes.get_mut(index)
152    }
153
154    fn set(&mut self, index: usize, gene: Self::Gene) {
155        if let Some(slot) = self.nodes.get_mut(index) {
156            *slot = gene;
157        }
158    }
159
160    fn iter(&self) -> impl Iterator<Item = &Self::Gene> {
161        self.nodes.iter()
162    }
163
164    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Gene> {
165        self.nodes.iter_mut()
166    }
167
168    fn len(&self) -> usize {
169        self.nodes.len()
170    }
171}
172
173impl<T> Valid for GraphChromosome<T> {
174    #[inline]
175    fn is_valid(&self) -> bool {
176        self.nodes.iter().all(|gene| gene.is_valid())
177    }
178}
179
180impl<T> AsRef<[GraphNode<T>]> for GraphChromosome<T> {
181    fn as_ref(&self) -> &[GraphNode<T>] {
182        &self.nodes
183    }
184}
185
186impl<T> AsMut<[GraphNode<T>]> for GraphChromosome<T> {
187    fn as_mut(&mut self) -> &mut [GraphNode<T>] {
188        &mut self.nodes
189    }
190}
191
192impl<T: PartialEq> PartialEq for GraphChromosome<T> {
193    fn eq(&self, other: &Self) -> bool {
194        self.nodes == other.nodes
195    }
196}
197
198impl<T: Hash> Hash for GraphChromosome<T> {
199    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
200        for node in self.as_ref() {
201            node.hash(state);
202        }
203    }
204}
205
206impl<T> From<Vec<GraphNode<T>>> for GraphChromosome<T> {
207    fn from(nodes: Vec<GraphNode<T>>) -> Self {
208        GraphChromosome {
209            nodes,
210            store: None,
211            max_nodes: None,
212        }
213    }
214}
215
216impl<T, I> From<(I, NodeStore<T>)> for GraphChromosome<T>
217where
218    I: IntoIterator<Item = GraphNode<T>>,
219{
220    fn from((iter, store): (I, NodeStore<T>)) -> Self {
221        GraphChromosome {
222            nodes: iter.into_iter().collect(),
223            store: Some(store),
224            max_nodes: None,
225        }
226    }
227}
228
229impl<T> FromIterator<GraphNode<T>> for GraphChromosome<T> {
230    fn from_iter<I: IntoIterator<Item = GraphNode<T>>>(iter: I) -> Self {
231        GraphChromosome {
232            nodes: iter.into_iter().collect(),
233            store: None,
234            max_nodes: None,
235        }
236    }
237}
238
239impl<T> IntoIterator for GraphChromosome<T> {
240    type Item = GraphNode<T>;
241    type IntoIter = std::vec::IntoIter<GraphNode<T>>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.nodes.into_iter()
245    }
246}
247
248impl<T: Debug> Debug for GraphChromosome<T> {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        writeln!(f, "Graph {{")?;
251        for node in self.as_ref() {
252            writeln!(f, "  {:?},", node)?;
253        }
254        write!(f, "}}")
255    }
256}