Compare commits

..

4 Commits

4 changed files with 424 additions and 5 deletions

View File

@ -426,18 +426,77 @@ make_instruction_clone!(exec, int, _size, Gene, 1);
/// Returns a nested element inside a block based on an int. /// Returns a nested element inside a block based on an int.
pub fn _extract(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> { pub fn _extract(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
Some(match vals[0].clone() { match vals[0].clone() {
Gene::Block(val) => { block @ Gene::Block(_) => {
if *val.len() == 0 { let block_len = block.rec_len();
if block_len == 0 {
return None; return None;
} else { } else {
let ndx = (auxs[0] % *val.len()).abs(); let ndx = (auxs[0] % block_len as i128).abs() as usize;
// @TODO: Finish this later! Some(vals[0].clone().code_at_point(ndx)?)
} }
} }
val => Some(val),
}
}
make_instruction_aux!(code, code, _extract, Gene, 1, int, 1, i128);
/// Inserts a gene at a given position in into the top block based off an
/// int from the top of the int stack. The top code item is coerced into a block
/// if needed.
pub fn _insert(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
let mut block = match vals[0].clone() {
iblock @ Gene::Block(_) => iblock,
val => Gene::Block(Box::new(vec![val])),
};
if block.rec_len() == 0 {
return _combine(vec![block, vals[1].clone()]);
}
let ndx = auxs[0].abs() as usize % block.rec_len();
block.with_code_inserted_at_point(vals[1].clone(), ndx);
Some(block)
}
make_instruction_aux!(code, code, _insert, Gene, 2, int, 1, i128);
/// Pushes the first position of the 2nd code item within the top code item.
/// If not found, pushes -1. If top code item isn't a block, returns 0 if top
/// two code items equal, -1 otherwise.
pub fn _first_position(vals: Vec<Gene>) -> Option<i128> {
let bad_cond: bool = match &vals[0] {
Gene::Block(val) => val.len() == 0,
_ => true,
};
if bad_cond {
if vals[0] == vals[1] {
return Some(0);
}
} else {
match &vals[0] {
Gene::Block(val) => {
for (idx, el) in val.iter().enumerate() {
if el == &vals[1] {
return Some(idx as i128);
}
}
}
_ => panic!("Error: Invariant of only a block failed in _first_position!"),
}
}
Some(-1)
}
make_instruction_clone!(code, int, _first_position, Gene, 2);
/// Reverses the top block. Does nothing if not a block.
pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> {
Some(match vals[0].clone() {
Gene::Block(mut val) => {
val.reverse();
Gene::Block(val)
}
val => val, val => val,
}) })
} }
make_instruction_clone!(code, code, _reverse, Gene, 1);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -1027,4 +1086,160 @@ mod tests {
code_size(&mut test_state); code_size(&mut test_state);
assert_eq!(vec![2], test_state.int); assert_eq!(vec![2], test_state.int);
} }
#[test]
fn extract_test() {
let mut test_state = EMPTY_STATE;
let test_code = vec![Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
]))];
test_state.code = test_code.clone();
test_state.int = vec![0];
code_member(&mut test_state);
assert_eq!(test_code.clone(), test_state.code);
test_state.code = test_code.clone();
test_state.int = vec![2];
code_extract(&mut test_state);
assert_eq!(vec![Gene::GeneInt(1)], test_state.code);
test_state.code = test_code.clone();
test_state.int = vec![4];
code_extract(&mut test_state);
assert_eq!(vec![Gene::GeneInt(4)], test_state.code);
test_state.code = test_code.clone();
test_state.int = vec![9];
code_extract(&mut test_state);
assert_eq!(
vec![Gene::Block(Box::new(vec![Gene::GeneBoolean(false)]))],
test_state.code
);
}
#[test]
fn insert_test() {
let mut test_state = EMPTY_STATE;
test_state.code = vec![
Gene::GeneInt(20),
Gene::Block(Box::new(vec![
Gene::GeneInt(1),
Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])),
])),
];
test_state.int = vec![1];
let inserted_block = vec![Gene::Block(Box::new(vec![
Gene::GeneInt(1),
Gene::GeneInt(20),
Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])),
]))];
code_insert(&mut test_state);
assert_eq!(inserted_block, test_state.code);
test_state.code = vec![
Gene::GeneInt(20),
Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
])),
];
test_state.int = vec![4];
let inserted_block = vec![Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneInt(20),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
]))];
code_insert(&mut test_state);
assert_eq!(inserted_block, test_state.code);
}
#[test]
fn first_position_test() {
let mut test_state = EMPTY_STATE;
test_state.code = vec![
Gene::GeneInt(20),
Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::GeneInt(20),
Gene::GeneFloat(dec!(6.0)),
])),
];
code_first_position(&mut test_state);
assert_eq!(vec![2], test_state.int);
test_state.int.clear();
test_state.code = vec![Gene::GeneBoolean(false), Gene::GeneBoolean(false)];
code_first_position(&mut test_state);
assert_eq!(vec![0], test_state.int);
test_state.int.clear();
test_state.code = vec![Gene::GeneBoolean(false), Gene::GeneBoolean(true)];
code_first_position(&mut test_state);
assert_eq!(vec![-1], test_state.int);
test_state.int.clear();
test_state.code = vec![
Gene::GeneInt(-6),
Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::GeneInt(20),
Gene::GeneFloat(dec!(6.0)),
])),
];
code_first_position(&mut test_state);
assert_eq!(vec![-1], test_state.int);
test_state.int.clear();
}
#[test]
fn code_reverse_test() {
let mut test_state = EMPTY_STATE;
test_state.code = vec![Gene::GeneInt(1)];
code_reverse(&mut test_state);
assert_eq!(vec![Gene::GeneInt(1)], test_state.code);
test_state.code = vec![Gene::Block(Box::new(vec![
Gene::GeneBoolean(false),
Gene::GeneInt(1),
Gene::GeneInt(20),
]))];
code_reverse(&mut test_state);
assert_eq!(
vec![Gene::Block(Box::new(vec![
Gene::GeneInt(20),
Gene::GeneInt(1),
Gene::GeneBoolean(false),
]))],
test_state.code
);
}
} }

View File

@ -200,3 +200,4 @@ pub mod common;
pub mod logical; pub mod logical;
pub mod numeric; pub mod numeric;
pub mod utils; pub mod utils;
pub mod vector;

View File

@ -0,0 +1,36 @@
use crate::push::state::PushState;
use rust_decimal::Decimal;
pub fn _concat<T>(vals: Vec<Vec<T>>) -> Option<Vec<T>>
where
T: Clone,
{
let mut concat_vec = vals[0].clone();
concat_vec.extend(vals[1].clone().into_iter());
Some(concat_vec)
}
make_instruction_clone!(vector_int, vector_int, _concat, Vec<i128>, 2);
make_instruction_clone!(vector_float, vector_float, _concat, Vec<Decimal>, 2);
make_instruction_clone!(vector_string, vector_string, _concat, Vec<Vec<char>>, 2);
make_instruction_clone!(vector_boolean, vector_boolean, _concat, Vec<bool>, 2);
make_instruction_clone!(vector_char, vector_char, _concat, Vec<char>, 2);
make_instruction_clone!(string, string, _concat, Vec<char>, 2);
#[cfg(test)]
mod tests {
use super::*;
use crate::push::state::EMPTY_STATE;
#[test]
fn test_vector_concat() {
let mut test_state = EMPTY_STATE;
test_state.vector_int = vec![vec![4, 5, 6], vec![1, 2, 3]];
vector_int_concat(&mut test_state);
assert_eq!(vec![vec![1, 2, 3, 4, 5, 6]], test_state.vector_int);
test_state.string = vec![vec!['s', 't'], vec!['t', 'e']];
string_concat(&mut test_state);
assert_eq!(vec![vec!['t', 'e', 's', 't']], test_state.string);
}
}

View File

@ -54,3 +54,170 @@ pub enum Gene {
Block(Box<Vec<Gene>>), Block(Box<Vec<Gene>>),
CrossoverPadding, CrossoverPadding,
} }
impl Gene {
/// Returns the len of a gene. If the gene is a block, returns
/// the size of the block counting the size of nested blocks.
pub fn rec_len(&self) -> usize {
let mut size: usize = 0;
match self {
Gene::Block(val) => {
for el in val.iter() {
match el {
iblock @ Gene::Block(_) => size += iblock.rec_len() + 1,
_ => size += 1,
}
}
}
_ => size += 1,
};
size
}
/// Extracts code at a point in the genome. Recurses into a block
/// if necessary. Point is based on an int. Pulled straight from HushGP.
pub fn code_at_point(self, index: usize) -> Option<Gene> {
if index == 0 {
return Some(self);
}
let mut idx = index;
match self {
Gene::Block(val) => {
for el in val.iter() {
idx -= 1;
if idx == 0 {
return Some(el.clone());
}
match el {
iblock @ Gene::Block(_) => {
if let Some(next_depth) = iblock.clone().code_at_point(idx) {
return Some(next_depth);
}
idx -= iblock.rec_len();
}
_ => continue,
}
}
return None;
}
val => Some(val),
}
}
/// Insert an element into a block recursively counting along the way.
/// Depth first. Modifies in-place.
pub fn with_code_inserted_at_point(&mut self, gene: Gene, idx: usize) {
if idx > self.rec_len() {
match self {
Gene::Block(val) => val.push(gene.clone()),
_ => {
panic!("Error: self must be a block for with_code_inserted_at_point to work!")
}
}
}
let _ = self.attempt_code_insert(gene, idx);
}
/// Attempts to insert an item into a block.
fn attempt_code_insert(&mut self, gene: Gene, index: usize) -> bool {
let mut idx = index;
match self {
Gene::Block(val) => {
for (n, el) in val.iter_mut().enumerate() {
if idx == 0 {
val.insert(n, gene.clone());
return true;
}
match el {
iblock @ Gene::Block(_) => {
// This line has side effects on iblock if inserts properly.
let success = iblock.attempt_code_insert(gene.clone(), idx - 1);
if success {
return true;
}
idx -= iblock.rec_len() + 1
}
_ => (),
}
idx -= 1;
}
if idx == 0 {
return true;
}
false
}
_ => panic!("Error: self must be a block for attempt_code_insert to work!"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rec_len_test() {
let block = Gene::Block(Box::new(vec![
Gene::GeneInt(1),
Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])),
]));
assert_eq!(4, block.rec_len());
let block = Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
]));
assert_eq!(10, block.rec_len());
let block = Gene::Block(Box::new(vec![]));
assert_eq!(0, block.rec_len());
}
#[test]
fn insert_test() {
let mut block = Gene::Block(Box::new(vec![
Gene::GeneInt(1),
Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])),
]));
let inserted_block = Gene::Block(Box::new(vec![
Gene::GeneInt(1),
Gene::GeneInt(20),
Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])),
]));
block.with_code_inserted_at_point(Gene::GeneInt(20), 1);
assert_eq!(inserted_block, block);
let mut block = Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
]));
let inserted_block = Gene::Block(Box::new(vec![
Gene::GeneBoolean(true),
Gene::GeneInt(1),
Gene::Block(Box::new(vec![
Gene::GeneInt(4),
Gene::GeneInt(20),
Gene::GeneFloat(dec!(6.0)),
Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])),
])),
Gene::GeneInt(10),
Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])),
]));
block.with_code_inserted_at_point(Gene::GeneInt(20), 4);
assert_eq!(inserted_block, block);
}
}