Skip to content

Commit f7d9285

Browse files
committed
Merge branch 'develop'
2 parents 6c3a3ab + d36d1b2 commit f7d9285

File tree

29 files changed

+751
-534
lines changed

29 files changed

+751
-534
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ homepage = "https://pkalivas.github.io/radiate/"
6363

6464
[workspace.dependencies]
6565
rand = "0.9.2"
66-
pyo3 = "0.26.0"
67-
numpy ="0.26.0"
66+
pyo3 = "0.27.1"
67+
numpy ="0.27.0"
6868
rayon = "1.11.0"
6969
serde = { version = "1.0.228", features = ["derive"] }
7070
serde_json = { version = "1.0.143" }

crates/radiate-core/src/evaluator.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ where
267267
}
268268
}
269269

270-
// integer ceiling division to determine batch size - number of individuals per batch
271270
let num_workers = self.executor.num_workers();
272271
let batch_size = (pairs.len() + num_workers - 1) / num_workers;
273272

@@ -305,7 +304,6 @@ where
305304
.collect(),
306305
);
307306

308-
// replace the genotypes and add their associated scores
309307
let mut count = 0;
310308
for (indices, scores, genotypes) in results {
311309
count += indices.len();

crates/radiate-core/src/genome/chromosomes/bit.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::fmt::{Debug, Display};
2424
/// let gene = gene.with_allele(allele);
2525
/// ```
2626
///
27-
#[derive(Clone, PartialEq, Debug)]
27+
#[derive(Clone, PartialEq, Debug, Default)]
2828
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2929
pub struct BitGene {
3030
allele: bool,
@@ -65,12 +65,6 @@ impl Gene for BitGene {
6565
/// Because a [`BitGene`] is either `true` or `false` it is always valid.
6666
impl Valid for BitGene {}
6767

68-
impl Default for BitGene {
69-
fn default() -> Self {
70-
Self::new()
71-
}
72-
}
73-
7468
impl Display for BitGene {
7569
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7670
write!(f, "{}", if self.allele { 1 } else { 0 })

crates/radiate-engines/src/builder/evaluators.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ where
2424
self
2525
}
2626

27+
#[cfg(feature = "rayon")]
28+
pub fn parallel(self) -> Self {
29+
self.executor(Executor::WorkerPool)
30+
}
31+
2732
pub fn executor(mut self, executor: Executor) -> Self {
2833
let executor = Arc::new(executor);
2934
self.params.evaluation_params = EvaluationParams {

crates/radiate-engines/src/builder/objectives.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ where
2626
self
2727
}
2828

29-
pub fn multi_objective(mut self, objectives: Vec<Optimize>) -> GeneticEngineBuilder<C, T> {
30-
self.params.optimization_params.objectives = Objective::Multi(objectives);
29+
pub fn multi_objective(
30+
mut self,
31+
objectives: impl Into<Vec<Optimize>>,
32+
) -> GeneticEngineBuilder<C, T> {
33+
self.params.optimization_params.objectives = Objective::Multi(objectives.into());
3134
self
3235
}
3336

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/// [Eval] trait is used to evaluate a [Tree] or [Graph] of node's.
2+
/// It is implemented directly on the GP structures to allow for easy and dynamic
3+
/// evaluation of the structures with a given input.
4+
///
5+
/// The [Eval] trait and subsequent method is used to transform the `Input` into
6+
/// the `Output`. This is extremely useful for evaluating the [Graph] or [Tree] with a given input
7+
/// as traversing each can be very slow or sometimes cumbersome to do manually.
8+
///
9+
/// # Example
10+
/// ```rust
11+
/// use radiate_gp::{Op, Eval, TreeNode};
12+
///
13+
/// let root = TreeNode::new(Op::add())
14+
/// .attach(
15+
/// TreeNode::new(Op::mul())
16+
/// .attach(TreeNode::new(Op::constant(2.0)))
17+
/// .attach(TreeNode::new(Op::constant(3.0))),
18+
/// )
19+
/// .attach(
20+
/// TreeNode::new(Op::add())
21+
/// .attach(TreeNode::new(Op::constant(2.0)))
22+
/// .attach(TreeNode::new(Op::var(0))),
23+
/// );
24+
///
25+
/// // And the result of evaluating this tree with an input of `1` would be:
26+
/// let result = root.eval(&vec![1_f32]);
27+
/// assert_eq!(result, 9.0);
28+
/// ```
29+
/// This creates a `Tree` that looks like:
30+
/// ```text
31+
/// +
32+
/// / \
33+
/// * +
34+
/// / \ / \
35+
/// 2 3 2 x
36+
/// ```
37+
/// Where `x` is the first variable in the input.
38+
///
39+
/// This can also be thought of (and is functionally equivalent) as:
40+
/// ```text
41+
/// f(x) = (2 * 3) + (2 + x)
42+
/// ```
43+
pub trait Eval<I: ?Sized, O> {
44+
fn eval(&self, input: &I) -> O;
45+
}
46+
47+
pub trait EvalMut<I: ?Sized, O> {
48+
fn eval_mut(&mut self, input: &I) -> O;
49+
}

crates/radiate-gp/src/collections/graphs/eval.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ where
4040
///
4141
/// # Arguments
4242
/// * graph - The [Graph] to reduce.
43+
#[inline]
4344
pub fn new<N>(graph: &'a N) -> GraphEvaluator<'a, T, V>
4445
where
4546
N: AsRef<[GraphNode<T>]>,
Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod dot;
2+
pub mod eval;
23
pub mod factory;
34
pub mod format;
45
pub mod graphs;
@@ -7,6 +8,7 @@ pub mod store;
78
pub mod trees;
89

910
pub use dot::ToDot;
11+
pub use eval::{Eval, EvalMut};
1012
pub use factory::*;
1113
pub use format::*;
1214
pub use graphs::{
@@ -18,62 +20,3 @@ pub use store::{NodeStore, NodeValue};
1820
pub use trees::{
1921
HoistMutator, Tree, TreeChromosome, TreeCodec, TreeCrossover, TreeIterator, TreeNode,
2022
};
21-
/// [Eval] trait is used to evaluate a [Tree] or [Graph] of node's.
22-
/// It is implemented directly on the [Tree] and [TreeNode] types as well as
23-
/// on the [GraphEvaluator] struct.
24-
///
25-
/// The [Eval] trait and subsequent method is used to transform the `Input` into
26-
/// the `Output`. This is extremely useful for evaluating the [Graph] or [Tree] with a given input
27-
/// as traversing each can be very slow or sometimes cumbersome to do manually.
28-
///
29-
/// # Example
30-
/// ```rust
31-
/// use radiate_gp::{Op, Eval, TreeNode};
32-
///
33-
/// let root = TreeNode::new(Op::add())
34-
/// .attach(
35-
/// TreeNode::new(Op::mul())
36-
/// .attach(TreeNode::new(Op::constant(2.0)))
37-
/// .attach(TreeNode::new(Op::constant(3.0))),
38-
/// )
39-
/// .attach(
40-
/// TreeNode::new(Op::add())
41-
/// .attach(TreeNode::new(Op::constant(2.0)))
42-
/// .attach(TreeNode::new(Op::var(0))),
43-
/// );
44-
///
45-
/// // And the result of evaluating this tree with an input of `1` would be:
46-
/// let result = root.eval(&vec![1_f32]);
47-
/// assert_eq!(result, 9.0);
48-
/// ```
49-
/// This creates a `Tree` that looks like:
50-
/// ```text
51-
/// +
52-
/// / \
53-
/// * +
54-
/// / \ / \
55-
/// 2 3 2 x
56-
/// ```
57-
/// Where `x` is the first variable in the input.
58-
///
59-
/// This can also be thought of (and is functionally equivalent) as:
60-
/// ```text
61-
/// f(x) = (2 * 3) + (2 + x)
62-
/// ```
63-
pub trait Eval<I: ?Sized, O> {
64-
fn eval(&self, input: &I) -> O;
65-
}
66-
67-
pub trait EvalMut<I: ?Sized, O> {
68-
fn eval_mut(&mut self, input: &I) -> O;
69-
}
70-
71-
impl<I, O, T> EvalMut<I, O> for T
72-
where
73-
I: ?Sized,
74-
T: Eval<I, O>,
75-
{
76-
fn eval_mut(&mut self, input: &I) -> O {
77-
self.eval(input)
78-
}
79-
}

crates/radiate-gp/src/collections/trees/eval.rs

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
use super::Tree;
2-
use crate::{Eval, TreeNode, node::Node};
2+
use crate::{Eval, EvalMut, TreeNode, node::Node};
3+
4+
/// Implements the [Eval] trait for [`Tree<T>`] where `T` is `Eval<[V], V>`. All this really does is
5+
/// call the `eval` method on the root node of the [Tree]. The real work is
6+
/// done in the [TreeNode] implementation below.
7+
impl<T, V> Eval<[V], V> for Tree<T>
8+
where
9+
T: Eval<[V], V>,
10+
V: Clone,
11+
{
12+
#[inline]
13+
fn eval(&self, input: &[V]) -> V {
14+
self.root()
15+
.map(|root| root.eval(input))
16+
.unwrap_or_else(|| panic!("Tree has no root node."))
17+
}
18+
}
19+
20+
/// Implements the [EvalMut] trait for [`Tree<T>`] where `T` is `Eval<[V], V>`. This is primarily just a simple
21+
/// implementation to satisfy the [regression](crate::regression) use-case where we want to evaluate
22+
/// a `Tree` and return a `Vec` of results. Since a [`Tree<T>`] only has a single root node, we return
23+
/// a `Vec` with a single element. All it really does is call the `eval` method on the root node of the [Tree].
24+
impl<T, V> EvalMut<[V], Vec<V>> for Tree<T>
25+
where
26+
T: Eval<[V], V>,
27+
V: Clone,
28+
{
29+
#[inline]
30+
fn eval_mut(&mut self, input: &[V]) -> Vec<V> {
31+
vec![
32+
self.root()
33+
.map(|root| root.eval(input))
34+
.unwrap_or_else(|| panic!("Tree has no root node.")),
35+
]
36+
}
37+
}
338

439
/// Implements the [Eval] trait for `Vec<Tree<T>>`. This is a wrapper around a `Vec<Tree<T>>`
540
/// and allows for the evaluation of each [Tree] in the `Vec` with a single input.
@@ -17,33 +52,35 @@ where
1752
}
1853
}
1954

20-
/// Implements the [Eval] trait for `Vec<&TreeNode<T>>`. This is a wrapper around a `Vec<&TreeNode<T>>`
21-
/// and allows for the evaluation of each [TreeNode] in the `Vec` with a single input.
22-
/// The len of the input slice must equal the number of nodes in the `Vec`.
23-
impl<T, V> Eval<[V], Vec<V>> for Vec<&TreeNode<T>>
55+
/// Implements the [EvalMut] trait for `Vec<Tree<T>>`. This is a wrapper around a `Vec<Tree<T>>`
56+
/// and allows for the evaluation of each [Tree] in the `Vec` with a single input.
57+
/// This is useful for things like `Ensemble` models where multiple models are used to make a prediction.
58+
///
59+
/// This is a simple implementation that just maps over the `Vec` and calls [Eval] on each [Tree]. Just like
60+
/// the implementation of [`EvalMut<[V], Vec<V>>`] for [`Tree<T>`] above, this is primarily to satisfy the
61+
/// [regression](crate::regression) use-case.
62+
impl<T, V> EvalMut<[V], Vec<V>> for Vec<Tree<T>>
2463
where
2564
T: Eval<[V], V>,
2665
V: Clone,
2766
{
2867
#[inline]
29-
fn eval(&self, inputs: &[V]) -> Vec<V> {
30-
self.iter().map(|node| node.eval(inputs)).collect()
68+
fn eval_mut(&mut self, inputs: &[V]) -> Vec<V> {
69+
self.iter_mut().map(|tree| tree.eval(inputs)).collect()
3170
}
3271
}
3372

34-
/// Implements the [Eval] trait for [`Tree<T>`] where `T` is `Eval<[V], V>`. All this really does is
35-
/// call the `eval` method on the root node of the [Tree]. The real work is
36-
/// done in the [TreeNode] implementation below.
37-
impl<T, V> Eval<[V], V> for Tree<T>
73+
/// Implements the [Eval] trait for `Vec<&TreeNode<T>>`. This is a wrapper around a `Vec<&TreeNode<T>>`
74+
/// and allows for the evaluation of each [TreeNode] in the `Vec` with a single input.
75+
/// The len of the input slice must equal the number of nodes in the `Vec`.
76+
impl<T, V> Eval<[V], Vec<V>> for Vec<&TreeNode<T>>
3877
where
3978
T: Eval<[V], V>,
4079
V: Clone,
4180
{
4281
#[inline]
43-
fn eval(&self, input: &[V]) -> V {
44-
self.root()
45-
.map(|root| root.eval(input))
46-
.unwrap_or_else(|| panic!("Tree has no root node."))
82+
fn eval(&self, inputs: &[V]) -> Vec<V> {
83+
self.iter().map(|node| node.eval(inputs)).collect()
4784
}
4885
}
4986

crates/radiate-gp/src/collections/trees/node.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,26 @@ use std::fmt::Debug;
2222
///
2323
/// # Examples
2424
/// ```
25-
/// use radiate_gp::{collections::{TreeNode}, Arity};
25+
/// use radiate_gp::{collections::{TreeNode}, Arity, Node};
2626
///
2727
/// // Create a new node with value 42
2828
/// let node = TreeNode::new(42);
2929
///
3030
/// // Create a node with specific arity
3131
/// let node_with_arity = TreeNode::with_arity(42, Arity::Exact(2));
32+
/// let other_node_with_arity = TreeNode::from((42, Arity::Exact(2)));
33+
///
34+
/// assert_eq!(node_with_arity.arity(), other_node_with_arity.arity());
3235
///
3336
/// // Create a node with children
3437
/// let node_with_children = TreeNode::with_children(42, vec![
3538
/// TreeNode::new(1),
3639
/// TreeNode::new(2)
3740
/// ]);
41+
/// let other_node_with_children = TreeNode::from((42, vec![
42+
/// TreeNode::new(1),
43+
/// TreeNode::new(2),
44+
/// ]));
3845
/// ```
3946
///
4047
/// # Node Types and [Arity]
@@ -341,6 +348,18 @@ impl<T: Debug> Debug for TreeNode<T> {
341348
}
342349
}
343350

351+
impl<T> From<(T, Arity)> for TreeNode<T> {
352+
fn from(value: (T, Arity)) -> Self {
353+
TreeNode::with_arity(value.0, value.1)
354+
}
355+
}
356+
357+
impl<T> From<(T, Vec<TreeNode<T>>)> for TreeNode<T> {
358+
fn from(value: (T, Vec<TreeNode<T>>)) -> Self {
359+
TreeNode::with_children(value.0, value.1)
360+
}
361+
}
362+
344363
macro_rules! impl_from {
345364
($($t:ty),+) => {
346365
$(

0 commit comments

Comments
 (0)