Compare commits

...

60 Commits
dev ... main

Author SHA1 Message Date
c7ec6e110b simplification test forgor 💀
All checks were successful
/ Test-Suite (push) Successful in 32s
2025-04-30 02:40:50 -05:00
d8698f7fdf simplification tested
Some checks failed
/ Test-Suite (push) Failing after 32s
2025-04-30 02:38:38 -05:00
f424a7bd3a progress on error function
Some checks failed
/ Test-Suite (push) Failing after 1m11s
2025-04-29 23:11:51 -05:00
35ad71ffc3 simplification, need to test 2025-04-29 17:47:01 -05:00
7e7d5523e9 about to start the main gp loop
All checks were successful
/ Test-Suite (push) Successful in 1m7s
2025-04-29 16:40:39 -05:00
e8f3910112 finish random instruction for now 2025-04-29 13:51:40 -05:00
f68e9577d6 start on random instruction
Some checks failed
/ Test-Suite (push) Failing after 30s
2025-04-29 01:41:38 -05:00
d51035270d gaussian noise utility/alternation done
Some checks failed
/ Test-Suite (push) Failing after 1m12s
2025-04-29 01:10:53 -05:00
eb69b6cfa5 change instruction list locations
All checks were successful
/ Test-Suite (push) Successful in 1m12s
2025-04-28 19:40:07 -05:00
37670b6416 move instruction list 2025-04-28 19:39:39 -05:00
b5c24b0cdb ignore dead code 2025-04-28 19:39:21 -05:00
fd7b1340ca _state and regex change to catch edge case 2025-04-28 19:39:08 -05:00
1617f7cdfc automatically generate lists 2025-04-28 19:32:27 -05:00
e99b6592b8 python development programs 2025-04-28 14:28:18 -05:00
cc68c0a2d3 cargo expand 2025-04-28 13:49:24 -05:00
467478b40e remove cargo expand 2025-04-28 12:31:52 -05:00
59d9460bf9 cargo expand 2025-04-28 12:30:33 -05:00
b941bf22cf try this 2025-04-28 12:29:06 -05:00
99f9c84c19 fix typo 2025-04-28 12:27:24 -05:00
ef25019fa2 see what happens if use .nightly 2025-04-28 12:26:44 -05:00
ea257cc94d too nightly for now 2025-04-28 12:22:00 -05:00
ab0c197ea5 see if toolchain.toml does anything 2025-04-28 12:21:44 -05:00
291206d566 complete beta profile 2025-04-28 12:17:49 -05:00
7dcbd31a00 nightly devshell 2025-04-28 12:15:00 -05:00
9c9126b3a8 polars for type ease, more done
Some checks failed
/ Test-Suite (push) Failing after 1m18s
2025-04-28 01:00:49 -05:00
be4bf37a35 rust-gdb testing
Some checks failed
/ Test-Suite (push) Failing after 29s
2025-04-27 17:43:35 -05:00
052c40ee60 comment out and pushy_to_push test start 2025-04-27 17:43:10 -05:00
2c2885995e change to beta 2025-04-26 22:36:50 -05:00
584cbe097e comment for why pygments is three
Some checks failed
/ Test-Suite (push) Failing after 26s
2025-04-26 21:48:49 -05:00
aa01060331 add pygments 2025-04-26 21:48:21 -05:00
8c0cba72e6 gdb_history file
Some checks failed
/ Test-Suite (push) Failing after 28s
2025-04-26 21:44:43 -05:00
98acf470d6 putting gdb-dashboard in dotfiles 2025-04-26 21:42:25 -05:00
bf026b3900 need to test plushy->push, no more compiler warnings (for now)
Some checks failed
/ Test-Suite (push) Failing after 28s
2025-04-24 23:01:07 -05:00
4b5d747441 add bacon 2025-04-24 22:44:36 -05:00
580d498dff pygments built in
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-24 19:09:41 -05:00
576402a893 python3.13 2025-04-24 19:08:23 -05:00
0423d3c106 gdb dashboard 2025-04-24 19:07:17 -05:00
b6619439ec no jetbrains
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-24 19:05:14 -05:00
4c94286edb add gdb 2025-04-23 23:22:02 -05:00
678fa04870 no debugger for now 2025-04-23 23:12:34 -05:00
af0ec5a316 try this 2025-04-23 23:03:29 -05:00
a25ca653f4 stdlib 2025-04-23 23:01:46 -05:00
50acb4d744 typo 2025-04-23 22:59:12 -05:00
c694992273 add rust-std 2025-04-23 22:58:17 -05:00
364eb53962 try rust-bin path 2025-04-23 22:54:45 -05:00
89dcab6287 try stable 2025-04-23 21:45:59 -05:00
c0bf9b7ca0 updated lock file 2025-04-23 21:43:52 -05:00
2cf6561753 simplify 2025-04-23 21:39:53 -05:00
0c6b3151b2 allow unfree
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-23 19:11:29 -05:00
d85c82e4a7 add rust-rover
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-23 18:58:16 -05:00
68ac4ca48f Gonna switch back to nix
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-22 15:56:57 -05:00
925b539223 instruction list done
Some checks failed
/ Test-Suite (push) Has been cancelled
2025-04-21 19:52:50 -05:00
a74ae22110 remove from here too 2025-04-21 16:52:47 -05:00
f3ff9fd684 internal expansion failed, manually define functions in a list time 2025-04-21 16:36:16 -05:00
9d017eb6ab remove pub from _ functions 2025-04-21 16:35:08 -05:00
ce68a225ea nightly toolchain 2025-04-21 16:19:49 -05:00
6e2a99338b eager2 complicated 2025-04-21 13:12:40 -05:00
9e7620db82 stuck on how to put these functions into a list 2025-04-21 12:12:00 -05:00
55d8f450c6 removed unused uses 2025-04-20 18:17:17 -05:00
190663c429 Merge pull request 'Merge new instruction definitions' (#2) from dev into main
Some checks failed
/ Test-Suite (push) Failing after 19s
Reviewed-on: #2
2025-04-19 23:09:05 -05:00
28 changed files with 2149 additions and 165 deletions

3
.gitignore vendored
View File

@ -25,3 +25,6 @@ Cargo.lock
# Added by cargo # Added by cargo
/target /target
# GDB files
.gdb_history

View File

@ -4,7 +4,8 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
rand = "0.9.0" rand = "0.9.1"
paste = "1.0.15" paste = "1.0.15"
rust_decimal = { version = "1.37", features = ["macros", "maths"] } rust_decimal = { version = "1.37", features = ["macros", "maths"] }
rush_macro = { path = "rush_macro" } rush_macro = { path = "rush_macro" }
polars = { version = "0.46.0", features = ["lazy"]}

View File

@ -8,6 +8,18 @@ a better language to develop this in rather than Haskell.
This is truly following the test-driven development model This is truly following the test-driven development model
so each of the files should have its own tests module. so each of the files should have its own tests module.
# Building
`cargo build` and it'll build.
### Adding new instructions
When adding new instructions, a python script generates the function lists and places them
in `src/instructions/list.rs`. When creating new instructions, it is important to run
`scripts/instruction_list.py` to add newly created instructions to their corresponding function
lists. This script uses python 3.13. Must be ran from the root of the directory as
`python scripts/instruction_list.py`.
## Link for later ## Link for later
https://miroslavtushev.medium.com/does-my-sample-have-to-be-normally-distributed-for-a-t-test-7ee91aaaca2a https://miroslavtushev.medium.com/does-my-sample-have-to-be-normally-distributed-for-a-t-test-7ee91aaaca2a

60
flake.lock generated
View File

@ -1,54 +1,39 @@
{ {
"nodes": { "nodes": {
"flake-parts": { "flake-utils": {
"inputs": { "inputs": {
"nixpkgs-lib": "nixpkgs-lib" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1741352980, "lastModified": 1731533236,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "hercules-ci", "owner": "numtide",
"repo": "flake-parts", "repo": "flake-utils",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "hercules-ci", "owner": "numtide",
"repo": "flake-parts", "repo": "flake-utils",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1742422364, "lastModified": 1745234285,
"narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=", "narHash": "sha256-GfpyMzxwkfgRVN0cTGQSkTC0OHhEkv3Jf6Tcjm//qZ0=",
"owner": "nixos", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc", "rev": "c11863f1e964833214b767f4a369c6e6a7aba141",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": {
"locked": {
"lastModified": 1740877520,
"narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1736320768, "lastModified": 1736320768,
@ -67,7 +52,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
} }
@ -89,6 +74,21 @@
"repo": "rust-overlay", "repo": "rust-overlay",
"type": "github" "type": "github"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

106
flake.nix
View File

@ -1,81 +1,51 @@
{ {
description = "Simple Rush nix flake";
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
rust-overlay.url = "github:oxalica/rust-overlay"; rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
}; };
outputs = inputs: outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
inputs.flake-parts.lib.mkFlake { inherit inputs; } { flake-utils.lib.eachDefaultSystem (system:
systems = [ "x86_64-linux" ];
perSystem = { config, self', pkgs, lib, system, ... }:
let let
runtimeDeps = with pkgs; [ alsa-lib speechd ]; overlays = [ (import rust-overlay) ];
buildDeps = with pkgs; [ pkg-config rustPlatform.bindgenHook ]; pkgs = import nixpkgs {
devDeps = with pkgs; [ gdb ]; inherit system overlays;
config.allowUnfree = true;
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
msrv = cargoToml.package.rust-version;
rustPackage = features:
(pkgs.makeRustPlatform {
cargo = pkgs.rust-bin.stable.latest.minimal;
rustc = pkgs.rust-bin.stable.latest.minimal;
}).buildRustPackage {
inherit (cargoToml.package) name version;
src = ./.;
cargoLock.lockFile = ./Cargo.lock;
buildFeatures = features;
buildInputs = runtimeDeps;
nativeBuildInputs = buildDeps;
# Uncomment if your cargo tests require networking or otherwise
# don't play nicely with the Nix build sandbox:
# doCheck = false;
}; };
in
mkDevShell = rustc: {
pkgs.mkShell { devShells.default = with pkgs; mkShell {
buildInputs = [
rust-bin.beta.latest.complete
];
packages = with pkgs; [
gdb
bacon
python313Packages.pygments # for personal gdb-dashboard use
];
shellHook = '' shellHook = ''
export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc}
export SHELL=${pkgs.lib.getExe pkgs.bashInteractive} export SHELL=${pkgs.lib.getExe pkgs.bashInteractive}
''; '';
buildInputs = runtimeDeps; };
nativeBuildInputs = buildDeps ++ devDeps ++ [ rustc ]; devShells.nightly = with pkgs; mkShell {
buildInputs = [
(rust-bin.selectLatestNightlyWith (toolchain: toolchain.complete))
];
packages = with pkgs; [ packages = with pkgs; [
rust-analyzer gdb
lldb bacon
(vscode-with-extensions.override { python313
vscode = vscodium; python313Packages.pygments # for personal gdb-dashboard use
vscodeExtensions = with vscode-extensions; [ python313Packages.ruff
rust-lang.rust-analyzer cargo-expand
] ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [ ];
{ shellHook = ''
name = "vscode-lldb"; export SHELL=${pkgs.lib.getExe pkgs.bashInteractive}
publisher = "vadimcn"; '';
version = "1.11.4"; };
# sha256 = "1hp6gjh4xp2m1xlm1jsdzxw9d8frkiidhph6nvl24d0h8z34w49g";
sha256 = "ylWlqKSiqsOL1S4vaFKLDck1wGm155bEGGL4+sKdBF8=";
} }
]; );
})
];
};
in {
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [ (import inputs.rust-overlay) ];
};
packages.default = self'.packages.rush;
devShells.default = self'.devShells.stable;
packages.rush = (rustPackage "rush");
packages.rush-base = (rustPackage "");
devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith
(toolchain: toolchain.default)));
devShells.stable = (mkDevShell pkgs.rust-bin.stable.latest.default);
devShells.msrv = (mkDevShell pkgs.rust-bin.stable.${msrv}.default);
};
};
} }

View File

@ -4,9 +4,6 @@ use syn::parse_macro_input;
mod utils; mod utils;
/// This macro kinda goes super crazy mode
/// Here's how to use the macro:
///
/// `run_instruction!(function_name, output_stack, push state, any amount of /// `run_instruction!(function_name, output_stack, push state, any amount of
/// comma separated stacks by name ; (the semicolon instructs use whether the instruction /// comma separated stacks by name ; (the semicolon instructs use whether the instruction
/// has multiple outputs. If ; passed, assumes multiple, without assumes just one output))` /// has multiple outputs. If ; passed, assumes multiple, without assumes just one output))`
@ -30,7 +27,7 @@ mod utils;
/// Some(vec![x + y, x - y]) /// Some(vec![x + y, x - y])
/// } /// }
/// ///
/// run_instruction!(aux_iadd, int, state, int, int;); /// // rush_macro::run_instruction!(aux_iadd, int, _state, int, int;);
/// ``` /// ```
/// would have the ; placed at the end of the instruction. Check rush's `tests/instruction_test.rs` /// would have the ; placed at the end of the instruction. Check rush's `tests/instruction_test.rs`
/// file for an example using this code. /// file for an example using this code.

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@ -0,0 +1,85 @@
import subprocess
import sys
import re
from pathlib import Path
def run_cargo_expand(binary_name, path_to_module):
"""Run cargo expand and return its output."""
try:
result = subprocess.run(
["cargo", "expand", "--bin", binary_name, path_to_module],
capture_output=True,
text=True,
check=True
)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error running cargo expand: {e}", file=sys.stderr)
print(f"stderr: {e.stderr}", file=sys.stderr)
sys.exit(1)
except FileNotFoundError:
print("Error: cargo expand command not found. Make sure cargo-expand is installed.", file=sys.stderr)
print("You can install it with: cargo install cargo-expand", file=sys.stderr)
sys.exit(1)
def extract_functions_with_push_state(code):
"""Extract function names that have &mut PushState as their only parameter."""
# Look for function definitions with pattern: pub fn name(state: &mut PushState)
pattern = r'pub fn ([a-zA-Z0-9_]+)\s*\(\s*_?state\s*:\s*&mut\s+PushState\s*\)'
matches = re.findall(pattern, code)
return matches
def organize_functions(func_list) -> dict[str, list]:
categorized: dict[str, list] = {}
for func in func_list:
parts: list[str] = func.split("_")
if parts[0] == "vector":
categorized[f"vector_{parts[1]}"] = []
else:
categorized[parts[0]] = []
for func in func_list:
for key in categorized.keys():
if func.startswith(key):
categorized[key].append(func)
return categorized
if __name__ == "__main__":
expanded_code: list = []
for rs_file in Path("src/instructions/").iterdir():
if rs_file.name not in ["mod.rs", "utils.rs", "list.rs"]:
expanded_code.append(run_cargo_expand("rush", f"instructions::{rs_file.stem}"))
function_names: list = []
for code in expanded_code:
function_names.extend(extract_functions_with_push_state(code))
categorized_funcs: dict[str, list] = organize_functions(function_names)
with open("src/instructions/list.rs", "w") as f:
for rs_file in Path("src/instructions/").iterdir():
if rs_file.name not in ["mod.rs", "utils.rs", "list.rs"]:
f.write(f"use crate::instructions::{rs_file.stem}::*;\n")
f.write("use crate::push::state::PushState;\n")
f.write("\n")
for list_name in categorized_funcs.keys():
f.write("pub fn " + list_name + "_instructions() -> Vec<fn(&mut PushState)> {\n")
f.write(" vec![\n")
for func in categorized_funcs[list_name]:
f.write(f" {func},\n")
f.write(" ]\n")
f.write("}\n")
f.write("\n")
f.write("pub fn all_instructions() -> Vec<fn(&mut PushState)> {\n")
f.write(" let mut all_vec = vec![];\n")
for list_name in categorized_funcs.keys():
f.write(f" all_vec.extend({list_name}_instructions().iter());\n")
f.write(" all_vec\n")
f.write("}")

79
src/gp/args.rs Normal file
View File

@ -0,0 +1,79 @@
use crate::gp::selection::Selection;
use crate::gp::variation::Variation;
use crate::push::state::Gene;
use polars::prelude::*;
use rust_decimal::prelude::*;
use std::collections::HashMap;
#[derive(Clone, Copy)]
pub enum ClosingType {
Specified,
Balanced,
None,
}
#[allow(dead_code)]
pub struct PushArgs {
pub alignment_deviation: Decimal, // For alternation, std dev of deviation of index when alternating
pub alternation_rate: usize, // For alternation, prob of switching parents at each location. A number 0-100
pub closes: ClosingType, // How push should automatically place Gene::Close into a plushy
pub dont_end: bool, // If true, keep running until limit regardless of success
// pub downsample: bool, // Whether or not to downsample. TODO later with all the related args
pub elitism: bool, // Whether to always add the best individual to next generation
pub error_function: Option<fn(&PushArgs, &DataFrame, Vec<Gene>) -> Vec<Decimal>>, // The error function
pub instructions: Option<Vec<Gene>>, // Instructions to use in a run
pub max_generations: usize, // Max amount of generations
pub max_init_plushy_size: usize, // max initial plushy size
pub max_stack_size: usize, // max size a stack is allowed to reach during execution
pub parent_selection: Selection, // Selection to use, TODO change this later.
pub pop_size: usize, // Population size
pub replacement_rate: f64, // For uniform replacement, rate items replaced
pub use_simplification: bool, // Whether to use simplification at end of run
pub simplification_k: usize, // Max amt of genes to attempt removal during one round of simplification process
pub simplification_steps: usize, // How many attempts to find simplified genomes
pub simplification_verbose: bool, // Whether to send extra messages about simplification or not
pub solution_error_threshold: Decimal, // Max total error for solutions
pub use_single_thread: bool, // if true, only single threaded
pub step_limit: usize, // Amount of steps a push interpreter can run for
pub testing_data: Option<DataFrame>, // The testing data, must be formatted the same as training data
pub tournament_size: usize, // Tournament size for tournament selection
pub training_data: Option<DataFrame>, // The training data, must be formatted the same as testing data
pub umad_rate: f64, // addition rate (deletion rate derived) for UMAD
pub variation: HashMap<Variation, f64>, // genetic operators and probability for use. should sum to 1,
}
impl PushArgs {
/// Holds the default arguments
pub fn new() -> Self {
let mut map = HashMap::new();
map.insert(Variation::UMAD, 1.0);
Self {
alignment_deviation: dec!(2.0),
alternation_rate: 10,
closes: ClosingType::Specified,
dont_end: false,
elitism: false,
error_function: None,
instructions: None,
max_generations: 1000,
max_init_plushy_size: 100,
max_stack_size: 100,
parent_selection: Selection::Lexicase,
pop_size: 1000,
replacement_rate: 0.1,
use_simplification: true,
simplification_k: 4,
simplification_steps: 1000,
simplification_verbose: true,
use_single_thread: false,
solution_error_threshold: dec!(0.0),
step_limit: 1000,
testing_data: None,
tournament_size: 5,
training_data: None,
umad_rate: 0.1,
variation: map,
}
}
}

194
src/gp/genome.rs Normal file
View File

@ -0,0 +1,194 @@
use crate::instructions::code::{
exec_do_count, exec_do_range, exec_do_times, exec_do_while, exec_if, exec_when, exec_while,
};
use crate::instructions::common::{
exec_dup, exec_dup_times, exec_pop, exec_rotate, exec_shove, exec_swap,
};
use crate::instructions::vector::{
string_iterate, vector_boolean_iterate, vector_char_iterate, vector_float_iterate,
vector_int_iterate, vector_string_iterate,
};
use crate::push::state::Gene;
use crate::push::state::Gene::StateFunc;
use rand::prelude::*;
use std::collections::HashMap;
use std::sync::LazyLock;
/// Generates a random plushy.
pub fn make_random_plushy(
genes: Vec<Gene>,
max_init_plushy_size: usize,
mut rng: impl Rng,
) -> Vec<Gene> {
let plushy_size = rng.random_range(0..=max_init_plushy_size);
let mut plushy = Vec::with_capacity(plushy_size);
for _ in 0..plushy_size {
plushy.push(genes[rng.random_range(0..genes.len())].clone());
}
plushy
}
/// A map of genes to their number of blocks they open.
pub static OPEN_MAP: LazyLock<HashMap<Gene, u8>> = LazyLock::new(|| {
let mut temp_map = HashMap::default();
temp_map.insert(StateFunc(exec_dup), 1u8);
temp_map.insert(StateFunc(exec_dup_times), 1u8);
temp_map.insert(StateFunc(exec_pop), 1u8);
temp_map.insert(StateFunc(exec_rotate), 3u8);
temp_map.insert(StateFunc(exec_shove), 1u8);
temp_map.insert(StateFunc(exec_swap), 2u8);
temp_map.insert(StateFunc(exec_if), 2u8);
temp_map.insert(StateFunc(exec_when), 1u8);
temp_map.insert(StateFunc(exec_while), 1u8);
temp_map.insert(StateFunc(exec_do_while), 1u8);
temp_map.insert(StateFunc(exec_do_range), 1u8);
temp_map.insert(StateFunc(exec_do_count), 1u8);
temp_map.insert(StateFunc(exec_do_times), 1u8);
//exec_k, 2
//exec_s 3
//exec_y 1
temp_map.insert(StateFunc(string_iterate), 1u8);
temp_map.insert(StateFunc(vector_int_iterate), 1u8);
temp_map.insert(StateFunc(vector_float_iterate), 1u8);
temp_map.insert(StateFunc(vector_string_iterate), 1u8);
temp_map.insert(StateFunc(vector_boolean_iterate), 1u8);
temp_map.insert(StateFunc(vector_char_iterate), 1u8);
temp_map
});
fn has_openers(genes: &[Gene]) -> bool {
for gene in genes {
if is_opener(gene) {
return true;
}
}
false
}
fn is_opener(gene: &Gene) -> bool {
match gene {
Gene::Open(_) => return true,
_ => (),
}
false
}
fn get_opener_count(gene: &Gene) -> &u8 {
match gene {
Gene::Open(val) => val,
_ => &0u8,
}
}
fn dec_opener(gene: Gene) -> Gene {
match gene {
Gene::Open(val) => Gene::Open(val - 1),
_ => gene,
}
}
/// Converts a plushy to a push program.
pub fn plushy_to_push(genes: Vec<Gene>) -> Vec<Gene> {
let mut plushy_buffer: Vec<Gene> = Vec::with_capacity(genes.len() * 2);
for gene in genes.into_iter() {
let open = OPEN_MAP.get(&gene);
plushy_buffer.push(gene);
if let Some(amt) = open {
plushy_buffer.push(Gene::Open(*amt))
}
}
let mut push_buffer = Vec::with_capacity(plushy_buffer.len());
loop {
// recur with one more close if there are openers
if plushy_buffer.is_empty() && has_openers(&push_buffer) {
plushy_buffer.push(Gene::Close);
} else if plushy_buffer.is_empty() {
return push_buffer;
} else {
let first_gene = plushy_buffer.remove(0);
match &first_gene {
Gene::Close => {
if has_openers(&push_buffer) {
let mut index: Option<usize> = None;
let mut opener: Option<Gene> = None;
// not the most optimal iterating through the entire genome.
// Will do for now.
for (ndx, el) in push_buffer.clone().into_iter().enumerate() {
if is_opener(&el) {
index = Some(ndx);
opener = Some(el);
}
}
let post_open: Vec<_> = push_buffer.drain((index.unwrap() + 1)..).collect();
push_buffer.pop(); // Pop the close here
push_buffer.push(Gene::Block(post_open));
if get_opener_count(&opener.clone().unwrap()) > &1u8 {
let opener_new = dec_opener(opener.unwrap().clone());
push_buffer.push(opener_new);
}
}
}
_ => push_buffer.push(first_gene),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// use crate::instructions::vector::{string_iterate, vector_float_maximum};
use crate::instructions::common::*;
use crate::instructions::numeric::*;
use crate::push::state::*;
// use crate::push::utils::most_genes;
// use rand::SeedableRng;
#[test]
fn make_random_plushy_test() {
// let rng = StdRng::seed_from_u64(42);
// let rand_plushy = make_random_plushy(most_genes(), 15, rng);
// let fin_result = vec![StateFunc(string_iterate), StateFunc(vector_float_maximum)];
// Make this consistent later
// assert_eq!(fin_result, rand_plushy);
}
#[test]
fn plushy_to_push_test() {
let plushy = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
];
let res_push = plushy_to_push(plushy);
assert_eq!(
res_push,
vec![
StateFunc(exec_swap),
Gene::Block(vec![Gene::StateFunc(float_tan), Gene::StateFunc(int_pop)]),
Gene::Block(vec![Gene::StateFunc(exec_flush)]),
Gene::StateFunc(boolean_pop),
]
);
let plushy = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
];
let res_push = plushy_to_push(plushy);
assert_eq!(
res_push,
vec![
StateFunc(exec_swap),
Gene::Block(vec![Gene::StateFunc(float_tan), Gene::StateFunc(int_pop)]),
Gene::Block(vec![]),
]
)
}
}

9
src/gp/individual.rs Normal file
View File

@ -0,0 +1,9 @@
use crate::push::state::Gene;
use rust_decimal::Decimal;
#[derive(Clone)]
pub struct Individual {
pub plushy: Vec<Gene>,
pub total_fitness: Option<Decimal>,
pub fitness_cases: Option<Vec<Decimal>>,
}

23
src/gp/mod.rs Normal file
View File

@ -0,0 +1,23 @@
use args::PushArgs;
pub mod args;
pub mod genome;
pub mod individual;
pub mod selection;
pub mod simplification;
pub mod utils;
pub mod variation;
pub fn gp_loop(push_args: PushArgs) -> bool {
let _pop_size = push_args.pop_size;
let _max_gens = push_args.max_generations;
let _error_func = push_args.error_function;
let _solution_error_threshold = push_args.solution_error_threshold;
let _dont_end = push_args.dont_end;
let _elitism = push_args.elitism;
let _training_data = push_args.training_data;
let _testing_data = push_args.testing_data;
let _simplification = push_args.use_simplification;
true
}

16
src/gp/selection.rs Normal file
View File

@ -0,0 +1,16 @@
use crate::gp::args::PushArgs;
use crate::gp::individual::Individual;
use rand::Rng;
pub enum Selection {
Lexicase,
Tournament,
}
pub fn select_parent(
_pop: Vec<Individual>,
_push_args: &PushArgs,
_rng: &mut impl Rng,
) -> Individual {
todo!()
}

89
src/gp/simplification.rs Normal file
View File

@ -0,0 +1,89 @@
use super::args::PushArgs;
use crate::push::state::Gene;
use polars::prelude::*;
use rand::Rng;
use rand::prelude::SliceRandom;
use rand::rng;
use rust_decimal::Decimal;
use std::collections::HashSet;
/// Takes k random indices from the given range
fn choose_random_k(k: usize, indices_count: usize, rng: &mut impl Rng) -> Vec<usize> {
let mut indices: Vec<usize> = (0..indices_count).collect();
indices.shuffle(rng);
indices.truncate(k);
indices
}
/// Deletes the values at the given set of indices
fn delete_at_indices<T: Clone>(indices: &[usize], plushy: &[T]) -> Vec<T> {
let indices_set: HashSet<usize> = indices.iter().cloned().collect();
plushy
.iter()
.enumerate()
.filter_map(|(i, item)| {
if !indices_set.contains(&i) {
Some(item.clone())
} else {
None
}
})
.collect()
}
/// Deletes k random instructions from the plushy
fn delete_k_random<T: Clone>(k: usize, plushy: &[T], rng: &mut impl Rng) -> Vec<T> {
let actual_k = std::cmp::min(k, plushy.len());
if actual_k == 0 {
return plushy.to_vec();
}
let indices = choose_random_k(actual_k, plushy.len(), rng);
delete_at_indices(&indices, plushy)
}
pub fn auto_simplify_plushy<F>(plushy: Vec<Gene>, error_func: F, push_args: PushArgs) -> Vec<Gene>
where
F: Fn(&PushArgs, &DataFrame, Vec<Gene>) -> Vec<Decimal>,
{
if push_args.simplification_verbose {
println!(
"{{ start_plushy_length: {}, k: {} }}",
plushy.len(),
push_args.simplification_k
);
}
let training_data = (&push_args)
.training_data
.clone()
.expect("Must provide training_data");
let mut curr_errors = error_func(&push_args, &training_data, plushy.clone());
let mut step = 0;
let mut curr_plushy = plushy;
while step < push_args.simplification_steps {
let mut rng = rng();
let random_k = rng.random_range(1..=push_args.simplification_k);
let new_plushy = delete_k_random(random_k, &curr_plushy, &mut rng);
let new_plushy_errors = error_func(&push_args, &training_data, new_plushy.clone());
if new_plushy_errors.iter().sum::<Decimal>() <= curr_errors.iter().sum() {
curr_plushy = new_plushy;
curr_errors = new_plushy_errors;
}
step += 1;
}
if push_args.simplification_verbose {
println!(
"{{ end_plushy_length: {}, k: {} }}",
curr_plushy.len(),
push_args.simplification_k
);
}
curr_plushy
}

268
src/gp/utils.rs Normal file
View File

@ -0,0 +1,268 @@
use crate::gp::args::ClosingType;
use crate::gp::genome::OPEN_MAP;
use crate::push::state::Gene;
use polars::prelude::*;
use rand::Rng;
use rand::seq::IndexedRandom;
use rust_decimal::prelude::*;
pub fn polars_to_gene(polars_value: &AnyValue) -> Gene {
match polars_value {
AnyValue::Int8(val) => Gene::GeneInt(*val as i128),
AnyValue::Int16(val) => Gene::GeneInt(*val as i128),
AnyValue::Int32(val) => Gene::GeneInt(*val as i128),
AnyValue::Int64(val) => Gene::GeneInt(*val as i128),
AnyValue::Int128(val) => Gene::GeneInt(*val as i128),
AnyValue::UInt8(val) => Gene::GeneInt(*val as i128),
AnyValue::UInt16(val) => Gene::GeneInt(*val as i128),
AnyValue::UInt32(val) => Gene::GeneInt(*val as i128),
AnyValue::UInt64(val) => Gene::GeneInt(*val as i128),
AnyValue::Float32(val) => Gene::GeneFloat(Decimal::from_f32(*val).unwrap()),
AnyValue::Float64(val) => Gene::GeneFloat(Decimal::from_f64(*val).unwrap()),
AnyValue::Boolean(val) => Gene::GeneBoolean(*val),
AnyValue::String(val) => Gene::GeneString(val.chars().collect()),
AnyValue::List(series) => match series.dtype() {
DataType::Int8 => {
let vec = series
.i8()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::Int16 => {
let vec = series
.i16()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::Int32 => {
let vec = series
.i32()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::Int64 => {
let vec = series
.i64()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::Int128 => {
let vec = series
.i64() // i64 will have to do
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::UInt8 => {
let vec = series
.u8()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::UInt16 => {
let vec = series
.u16()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::UInt32 => {
let vec = series
.u32()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::UInt64 => {
let vec = series
.u64()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorInt(vec)
}
DataType::Float32 => {
let vec = series
.f32()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| Decimal::from_f32(v).unwrap()))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorFloat(vec)
}
DataType::Float64 => {
let vec = series
.f64()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| Decimal::from_f64(v).unwrap()))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorFloat(vec)
}
DataType::Boolean => {
let vec = series
.bool()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as bool))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorBoolean(vec)
}
DataType::String => {
let vec = series
.str()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v.chars().collect()))
.collect::<Option<Vec<_>>>()
.unwrap();
Gene::GeneVectorString(vec)
}
_ => unimplemented!("Type {:?} not handled inside a vector", polars_value),
},
_ => unimplemented!("Type {:?} not handled", polars_value),
}
}
pub fn random_instruction(
instructions: Vec<Gene>,
closing_type: ClosingType,
rng: &mut impl Rng,
) -> Gene {
match closing_type {
ClosingType::Specified => return instructions.choose(rng).unwrap().clone(),
ClosingType::Balanced => {
let source: Vec<Gene> = instructions
.iter()
.filter(|instr| !matches!(instr, Gene::Close))
.cloned()
.collect();
let total_opens: usize = source
.iter()
.filter_map(|instr| {
if let Gene::StateFunc(_) = instr {
OPEN_MAP.get(instr).copied().map(|val| val as usize)
} else {
None
}
})
.sum();
let p = if source.is_empty() {
0.0
} else {
total_opens as f64 / source.len() as f64
};
// Return Close or a random instruction based on probability
if rng.random::<f64>() < p {
Gene::Close
} else {
source.choose(rng).unwrap().clone()
}
}
ClosingType::None => {
// Find multi-block instructions (those with opens > 1)
let multi_block_instructions: Vec<Gene> = instructions
.iter()
.filter(|instr| {
if let Gene::StateFunc(_) = instr {
OPEN_MAP.get(instr).map_or(false, |&opens| opens > 1)
} else {
false
}
})
.cloned()
.collect();
// Remove Close and multi-block instructions
let source: Vec<Gene> = instructions
.into_iter() // Take ownership of instructions
.filter(|instr| {
!matches!(instr, Gene::Close) && !multi_block_instructions.contains(instr)
})
.collect();
source.choose(rng).unwrap().clone()
}
}
}
pub fn gaussian_noise_factor(rng: &mut impl Rng) -> Decimal {
let u0f64: f64 = rng.random();
let u1f64: f64 = rng.random();
let u0: Decimal = FromPrimitive::from_f64(u0f64).unwrap();
let u1: Decimal = FromPrimitive::from_f64(u1f64).unwrap();
let u0 = if u0 == dec!(0.0) {
FromPrimitive::from_f64(f64::EPSILON).unwrap()
} else {
u0
};
(dec!(-2.0) * u0.ln()).sqrt().unwrap() * (dec!(2.0) * rust_decimal::Decimal::PI * u1).cos()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::instructions::code::*;
use crate::instructions::common::boolean_rotate;
use crate::instructions::vector::*;
use crate::push::utils::most_genes;
use rand::SeedableRng;
use rand::rngs::StdRng;
#[test]
fn random_instruction_test() {
let mut rng = StdRng::seed_from_u64(42);
let genes = most_genes();
let rand_instruction = random_instruction(genes.clone(), ClosingType::Specified, &mut rng);
assert_eq!(
Gene::StateFunc(vector_float_from_last_prim),
rand_instruction
);
let mut rng = StdRng::seed_from_u64(32038);
let rand_instruction = random_instruction(genes.clone(), ClosingType::Balanced, &mut rng);
assert_eq!(Gene::StateFunc(boolean_rotate), rand_instruction);
let mut rng = StdRng::seed_from_u64(3203890821);
let rand_instruction = random_instruction(genes, ClosingType::None, &mut rng);
assert_eq!(Gene::StateFunc(code_insert), rand_instruction);
}
}

509
src/gp/variation.rs Normal file
View File

@ -0,0 +1,509 @@
use crate::gp::args::PushArgs;
use crate::gp::individual::Individual;
use crate::gp::selection::select_parent;
use crate::gp::utils::gaussian_noise_factor;
use crate::push::state::Gene;
use rand::Rng;
use rust_decimal::Decimal;
use rust_decimal::prelude::ToPrimitive;
use std::collections::HashMap;
use std::iter::zip;
use super::args::ClosingType;
use super::utils::random_instruction;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Variation {
Crossover,
Alternation,
TailAlignedCrossover,
UniformAddition,
UniformReplacement,
UniformDeletion,
Reproduction,
UMAD,
}
fn crossover(plushy0: Vec<Gene>, plushy1: Vec<Gene>, mut rng: impl Rng) -> Vec<Gene> {
let mut shorter: Vec<Gene>;
let longer: Vec<Gene>;
let mut new_plushy: Vec<Gene> = vec![];
if plushy0.len() >= plushy1.len() {
shorter = plushy1;
longer = plushy0;
} else {
shorter = plushy0;
longer = plushy1;
}
for _ in 0..(longer.len() - shorter.len()) {
shorter.push(Gene::CrossoverPadding)
}
// Add genes here
for (sgene, lgene) in zip(shorter, longer) {
if rng.random_range(0..=99) < 50 {
new_plushy.push(sgene)
} else {
new_plushy.push(lgene)
}
}
new_plushy
.into_iter()
.filter(|gene| !matches!(gene, Gene::CrossoverPadding))
.collect()
}
fn tail_aligned_crossover(plushy0: Vec<Gene>, plushy1: Vec<Gene>, mut rng: impl Rng) -> Vec<Gene> {
let mut shorter: Vec<Gene>;
let longer: Vec<Gene>;
let mut new_plushy: Vec<Gene> = vec![];
if plushy0.len() >= plushy1.len() {
shorter = plushy1;
longer = plushy0;
} else {
shorter = plushy0;
longer = plushy1;
}
for _ in 0..(longer.len() - shorter.len()) {
shorter.insert(0, Gene::CrossoverPadding)
}
// Add genes here
for (sgene, lgene) in zip(shorter, longer) {
if rng.random_range(0..=99) < 50 {
new_plushy.push(sgene)
} else {
new_plushy.push(lgene)
}
}
new_plushy
.into_iter()
.filter(|gene| !matches!(gene, Gene::CrossoverPadding))
.collect()
}
fn alternation(
plushy0: Vec<Gene>,
plushy1: Vec<Gene>,
alternation_rate: usize,
alignment_deviation: Decimal,
mut rng: impl Rng,
) -> Vec<Gene> {
let mut use_plushy0: bool = true;
let mut result_plushy: Vec<Gene> = vec![];
let mut iteration_budget = plushy0.len() + plushy1.len();
let mut n: i128 = 0;
loop {
if use_plushy0 {
if n >= plushy0.len() as i128 {
return result_plushy;
}
} else {
if n >= plushy1.len() as i128 {
return result_plushy;
}
}
if iteration_budget <= 0 {
return result_plushy;
}
if rng.random_range(0..=99) < alternation_rate {
let pre_usize = alignment_deviation * gaussian_noise_factor(&mut rng);
n = 0i128.max(n + ToPrimitive::to_i128(&(pre_usize).round()).unwrap());
use_plushy0 = !use_plushy0;
} else {
result_plushy.push(if use_plushy0 {
plushy0[n as usize].clone()
} else {
plushy1[n as usize].clone()
});
n += 1;
}
iteration_budget -= 1;
}
}
fn uniform_addition(
plushy: Vec<Gene>,
instructions: Vec<Gene>,
umad_rate: f64,
closing_type: ClosingType,
rng: &mut impl Rng,
) -> Vec<Gene> {
let mut new_plushy: Vec<Gene> = vec![];
for gene in plushy {
if rng.random::<f64>() < umad_rate {
let new_instruction = random_instruction(instructions.clone(), closing_type, rng);
// Randomly decide order (original first or new first)
if rng.random::<bool>() {
new_plushy.push(gene);
new_plushy.push(new_instruction);
} else {
new_plushy.push(new_instruction);
new_plushy.push(gene);
}
} else {
new_plushy.push(gene);
}
}
new_plushy
}
fn uniform_replacement(
plushy: Vec<Gene>,
instructions: Vec<Gene>,
replacement_rate: f64,
closing_type: ClosingType,
rng: &mut impl Rng,
) -> Vec<Gene> {
plushy
.into_iter()
.map(|gene| {
if rng.random::<f64>() < replacement_rate {
// Replace with random instruction
random_instruction(instructions.to_vec(), closing_type, rng)
} else {
// Keep original gene
gene
}
})
.collect()
}
fn uniform_deletion(plushy: Vec<Gene>, umad_rate: f64, rng: &mut impl Rng) -> Vec<Gene> {
// If umad_rate is zero, return the original vector
if umad_rate == 0.0 {
return plushy;
}
// Calculate the adjusted deletion rate
let adjusted_rate = 1.0 / (1.0 + (1.0 / umad_rate));
// Filter the vector, keeping items that are either Gene::Skip or pass the random test
plushy
.into_iter()
.filter(|_| rng.random::<f64>() >= adjusted_rate)
.collect()
}
/// Selects a variation operator based on the probabilities
fn select_variation_op(variation_ops: &HashMap<Variation, f64>, r: f64) -> Variation {
let mut accum = 0.0;
for (op, prob) in variation_ops {
accum += prob;
if accum >= r {
return op.clone();
}
}
// Default to reproduction if no match (or probabilities don't sum to 1.0)
Variation::Reproduction
}
/// Creates a new individual based on an argmap variation
pub fn new_individual(pop: Vec<Individual>, argmap: &PushArgs, rng: &mut impl Rng) -> Individual {
// Select variation operator based on probabilities
let r = rng.random::<f64>();
let op = select_variation_op(&argmap.variation, r);
let plushy = match op {
Variation::Crossover => {
let parent1 = select_parent(pop.clone(), argmap, rng);
let parent2 = select_parent(pop, argmap, rng);
crossover(parent1.plushy, parent2.plushy, rng)
}
Variation::TailAlignedCrossover => {
let parent1 = select_parent(pop.clone(), argmap, rng);
let parent2 = select_parent(pop, argmap, rng);
tail_aligned_crossover(parent1.plushy, parent2.plushy, rng)
}
Variation::UniformAddition => {
let parent = select_parent(pop, argmap, rng);
uniform_addition(
parent.plushy.clone(),
argmap
.instructions
.clone()
.expect("Must provide instructions"),
argmap.umad_rate,
argmap.closes,
rng,
)
}
Variation::UniformReplacement => {
let parent = select_parent(pop, argmap, rng);
uniform_replacement(
parent.plushy.clone(),
argmap
.instructions
.clone()
.expect("Must provide instructions!"),
argmap.replacement_rate,
argmap.closes,
rng,
)
}
Variation::UniformDeletion => {
let parent = select_parent(pop, argmap, rng);
uniform_deletion(parent.plushy.clone(), argmap.umad_rate, rng)
}
Variation::Alternation => {
let parent1 = select_parent(pop.clone(), argmap, rng);
let parent2 = select_parent(pop, argmap, rng);
alternation(
parent1.plushy,
parent2.plushy,
argmap.alternation_rate,
argmap.alignment_deviation,
rng,
)
}
Variation::UMAD => {
let parent = select_parent(pop, argmap, rng);
let parent_plushy = parent.plushy.clone();
// Apply uniform addition followed by uniform deletion
let after_addition = uniform_addition(
parent_plushy,
argmap
.instructions
.clone()
.expect("Must provide instructions"),
argmap.umad_rate,
argmap.closes,
rng,
);
uniform_deletion(after_addition, argmap.umad_rate, rng)
}
Variation::Reproduction => {
let parent = select_parent(pop, argmap, rng);
parent.plushy.clone()
}
};
Individual {
plushy,
total_fitness: None,
fitness_cases: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::instructions::common::*;
use crate::instructions::numeric::*;
use crate::instructions::vector::*;
use crate::push::state::Gene;
use crate::push::utils::most_genes;
use rand::SeedableRng;
use rand::rngs::StdRng;
use rust_decimal::dec;
#[test]
fn crossover_test() {
let rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
Gene::StateFunc(vector_int_swap),
Gene::StateFunc(vector_char_pop),
];
let plushy1 = vec![
Gene::StateFunc(string_swap),
Gene::StateFunc(float_arctan),
Gene::StateFunc(char_pop),
Gene::GeneChar('a'),
Gene::StateFunc(code_flush),
Gene::GeneInt(1),
Gene::StateFunc(float_pop),
];
let res_plushy = crossover(plushy0, plushy1, rng);
assert_eq!(
vec![
Gene::StateFunc(string_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(char_pop),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
Gene::StateFunc(vector_char_pop),
],
res_plushy
)
}
#[test]
fn tail_aligned_crossover_test() {
let rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
Gene::StateFunc(vector_int_swap),
Gene::StateFunc(vector_char_pop),
];
let plushy1 = vec![
Gene::StateFunc(string_swap),
Gene::StateFunc(float_arctan),
Gene::StateFunc(char_pop),
Gene::GeneChar('a'),
Gene::StateFunc(code_flush),
Gene::GeneInt(1),
Gene::StateFunc(float_pop),
];
let res_plushy = tail_aligned_crossover(plushy0, plushy1, rng);
assert_eq!(
vec![
Gene::StateFunc(float_tan),
Gene::StateFunc(string_swap),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
Gene::GeneInt(1),
Gene::StateFunc(vector_char_pop),
],
res_plushy
)
}
#[test]
fn alternation_test() {
let rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::StateFunc(exec_flush),
Gene::Close,
Gene::StateFunc(boolean_pop),
Gene::StateFunc(vector_int_swap),
Gene::StateFunc(vector_char_pop),
];
let plushy1 = vec![
Gene::StateFunc(string_swap),
Gene::StateFunc(float_arctan),
Gene::StateFunc(char_pop),
Gene::GeneChar('a'),
Gene::StateFunc(code_flush),
Gene::GeneInt(1),
Gene::StateFunc(float_pop),
];
let res_plushy = alternation(plushy0, plushy1, 50, dec!(2.0), rng);
assert_eq!(
vec![
Gene::StateFunc(char_pop),
Gene::GeneChar('a'),
Gene::StateFunc(boolean_pop),
Gene::StateFunc(vector_int_swap),
Gene::StateFunc(vector_char_pop),
],
res_plushy
);
}
#[test]
fn uniform_addition_test() {
let mut rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
];
let res_plushy =
uniform_addition(plushy0, most_genes(), 0.75, ClosingType::Balanced, &mut rng);
assert_eq!(
vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_min),
Gene::StateFunc(float_tan),
Gene::Close,
Gene::StateFunc(int_pop),
Gene::StateFunc(int_yank_dup),
Gene::Close,
Gene::StateFunc(float_is_empty),
],
res_plushy
);
}
#[test]
fn uniform_replacement_test() {
let mut rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::Close,
Gene::GeneInt(1),
];
let res_plushy =
uniform_replacement(plushy0, most_genes(), 0.5, ClosingType::Balanced, &mut rng);
assert_eq!(
vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::StateFunc(vector_float_sort_reverse),
Gene::GeneInt(1),
],
res_plushy
);
}
#[test]
fn uniform_deletion_test() {
let mut rng = StdRng::seed_from_u64(42);
let plushy0 = vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::Close,
Gene::GeneInt(1),
];
let res_plushy = uniform_deletion(plushy0, 0.5, &mut rng);
assert_eq!(
vec![
Gene::StateFunc(exec_swap),
Gene::StateFunc(float_tan),
Gene::StateFunc(int_pop),
Gene::Close,
Gene::GeneInt(1),
],
res_plushy
);
}
}

View File

@ -1,15 +1,10 @@
use std::ops::Not;
use crate::push::state::{Gene, PushState};
use super::common::{code_from_exec, code_pop, int_pop}; use super::common::{code_from_exec, code_pop, int_pop};
use crate::push::state::{Gene, PushState};
use std::ops::Not;
/// Checks to see if a single gene is a block. /// Checks to see if a single gene is a block.
fn _is_block(a: Gene) -> Option<bool> { fn _is_block(a: Gene) -> Option<bool> {
Some(match a { Some(matches!(a, Gene::Block(_)))
Gene::Block(_) => true,
_ => false,
})
} }
/// Checks to see if a single gene is not a block. /// Checks to see if a single gene is not a block.
@ -321,7 +316,7 @@ pub fn code_map(state: &mut PushState) {
/// If top bool is true, execute top element of code/exec stack and skip the second. /// If top bool is true, execute top element of code/exec stack and skip the second.
/// If false, execute second element and skip the top. /// If false, execute second element and skip the top.
pub fn _if(a: Gene, b: Gene, cond: bool) -> Option<Gene> { fn _if(a: Gene, b: Gene, cond: bool) -> Option<Gene> {
Some(if cond { a } else { b }) Some(if cond { a } else { b })
} }
@ -349,7 +344,7 @@ pub fn exec_when(state: &mut PushState) {
/// Pushes true if the second code item is found within the first item. /// Pushes true if the second code item is found within the first item.
/// If the first item isn't a block, coerced into one. /// If the first item isn't a block, coerced into one.
pub fn _member(a: Gene, b: Gene) -> Option<bool> { fn _member(a: Gene, b: Gene) -> Option<bool> {
let block = match b { let block = match b {
Gene::Block(val) => val, Gene::Block(val) => val,
val => vec![val], val => vec![val],
@ -360,7 +355,7 @@ pub fn _member(a: Gene, b: Gene) -> Option<bool> {
/// Pushes the nth item of the top element of the code stack. /// Pushes the nth item of the top element of the code stack.
/// If top code item isn't a block, wrap one around it. /// If top code item isn't a block, wrap one around it.
pub fn _nth(a: Gene, idx: i128) -> Option<Gene> { fn _nth(a: Gene, idx: i128) -> Option<Gene> {
let gene_vec = match a { let gene_vec = match a {
Gene::Block(val) => val, Gene::Block(val) => val,
val => vec![val], val => vec![val],
@ -374,27 +369,21 @@ pub fn _nth(a: Gene, idx: i128) -> Option<Gene> {
} }
/// Pushes an empty block to the top of a stack. /// Pushes an empty block to the top of a stack.
pub fn _make_empty_block<T>() -> Option<Gene> { fn _make_empty_block<T>() -> Option<Gene> {
Some(Gene::Block(vec![])) Some(Gene::Block(vec![]))
} }
/// Checks to see if the top item on the code/exec stack is an empty block. /// Checks to see if the top item on the code/exec stack is an empty block.
/// True if is, False if not. /// True if is, False if not.
pub fn _is_empty_block(a: Gene) -> Option<bool> { fn _is_empty_block(a: Gene) -> Option<bool> {
Some(match a { Some(match a {
Gene::Block(val) => { Gene::Block(val) => val.is_empty(),
if val.is_empty() {
true
} else {
false
}
}
_ => false, _ => false,
}) })
} }
/// Returns the size of the top item on the code/exec stack. /// Returns the size of the top item on the code/exec stack.
pub fn _size(a: Gene) -> Option<i128> { fn _size(a: Gene) -> Option<i128> {
Some(match a.clone() { Some(match a.clone() {
Gene::Block(val) => val.len() as i128, Gene::Block(val) => val.len() as i128,
_ => 1, _ => 1,
@ -402,7 +391,7 @@ pub fn _size(a: Gene) -> Option<i128> {
} }
/// 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(a: Gene, idx: i128) -> Option<Gene> { fn _extract(a: Gene, idx: i128) -> Option<Gene> {
match &a { match &a {
block @ Gene::Block(_) => { block @ Gene::Block(_) => {
let block_len = block.rec_len(); let block_len = block.rec_len();
@ -420,7 +409,7 @@ pub fn _extract(a: Gene, idx: i128) -> Option<Gene> {
/// Inserts a gene at a given position in into the top block based off an /// 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 /// int from the top of the int stack. The top code item is coerced into a block
/// if needed. /// if needed.
pub fn _insert(a: Gene, b: Gene, idx: i128) -> Option<Gene> { fn _insert(a: Gene, b: Gene, idx: i128) -> Option<Gene> {
let mut block = match a.clone() { let mut block = match a.clone() {
iblock @ Gene::Block(_) => iblock, iblock @ Gene::Block(_) => iblock,
val => Gene::Block(vec![val]), val => Gene::Block(vec![val]),
@ -436,7 +425,7 @@ pub fn _insert(a: Gene, b: Gene, idx: i128) -> Option<Gene> {
/// Pushes the first position of the 2nd code item within the top code item. /// 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 /// If not found, pushes -1. If top code item isn't a block, returns 0 if top
/// two code items equal, -1 otherwise. /// two code items equal, -1 otherwise.
pub fn _first_position(a: Gene, b: Gene) -> Option<i128> { fn _first_position(a: Gene, b: Gene) -> Option<i128> {
let bad_cond: bool = match &a { let bad_cond: bool = match &a {
Gene::Block(val) => val.len() == 0, Gene::Block(val) => val.len() == 0,
_ => true, _ => true,
@ -461,7 +450,7 @@ pub fn _first_position(a: Gene, b: Gene) -> Option<i128> {
} }
/// Reverses the top block. Does nothing if not a block. /// Reverses the top block. Does nothing if not a block.
pub fn _reverse(a: Gene) -> Option<Gene> { fn _reverse(a: Gene) -> Option<Gene> {
Some(match a { Some(match a {
Gene::Block(mut val) => { Gene::Block(mut val) => {
val.reverse(); val.reverse();
@ -475,7 +464,7 @@ pub fn _reverse(a: Gene) -> Option<Gene> {
macro_rules! noop { macro_rules! noop {
($stack:ident, $name:ident) => { ($stack:ident, $name:ident) => {
paste::item! { paste::item! {
pub fn [< $stack $name >] (_: &mut PushState) { pub fn [< $stack $name >] (_state: &mut PushState) {
() ()
} }
} }

View File

@ -1,6 +1,5 @@
use std::cmp::{max, min};
use crate::push::state::{Gene, PushState}; use crate::push::state::{Gene, PushState};
use std::cmp::{max, min};
fn min_max_bounds(ndx: i128, length: usize) -> usize { fn min_max_bounds(ndx: i128, length: usize) -> usize {
max(0, min(ndx.unsigned_abs() as usize, length - 1)) max(0, min(ndx.unsigned_abs() as usize, length - 1))
@ -71,31 +70,31 @@ macro_rules! make_code {
} }
/// Duplicates an item /// Duplicates an item
pub fn _dup<T: Clone>(val: T) -> Option<Vec<T>> { fn _dup<T: Clone>(val: T) -> Option<Vec<T>> {
Some(vec![val.clone(), val]) Some(vec![val.clone(), val])
} }
pub fn _dup_times<T: Clone>(amt: i128, val: T) -> Option<Vec<T>> { fn _dup_times<T: Clone>(amt: i128, val: T) -> Option<Vec<T>> {
Some(vec![val; amt as usize]) Some(vec![val; amt as usize])
} }
/// Swaps two values /// Swaps two values
pub fn _swap<T: Clone>(a: T, b: T) -> Option<Vec<T>> { fn _swap<T>(a: T, b: T) -> Option<Vec<T>> {
Some(vec![a, b]) Some(vec![a, b])
} }
/// Rotates three values /// Rotates three values
pub fn _rotate<T>(a: T, b: T, c: T) -> Option<Vec<T>> { fn _rotate<T>(a: T, b: T, c: T) -> Option<Vec<T>> {
Some(vec![c, a, b]) Some(vec![c, a, b])
} }
/// Checks if two values are equal /// Checks if two values are equal
pub fn _equal<T: Eq>(a: T, b: T) -> Option<bool> { fn _equal<T: Eq>(a: T, b: T) -> Option<bool> {
Some(b == a) Some(b == a)
} }
/// Checks if two values are not equal /// Checks if two values are not equal
pub fn _not_equal<T: Clone + Eq>(a: T, b: T) -> Option<bool> { fn _not_equal<T: Eq>(a: T, b: T) -> Option<bool> {
Some(b != a) Some(b != a)
} }

609
src/instructions/list.rs Normal file
View File

@ -0,0 +1,609 @@
use crate::instructions::vector::*;
use crate::instructions::code::*;
use crate::instructions::numeric::*;
use crate::instructions::logical::*;
use crate::instructions::common::*;
use crate::push::state::PushState;
pub fn vector_int_instructions() -> Vec<fn(&mut PushState)> {
vec![
vector_int_iterate,
vector_int_concat,
vector_int_conj,
vector_int_conj_end,
vector_int_take_n,
vector_int_take_last_n,
vector_int_sub,
vector_int_first,
vector_int_from_first_prim,
vector_int_from_prim,
vector_int_last,
vector_int_from_last_prim,
vector_int_nth,
vector_int_from_nth_prim,
vector_int_rest,
vector_int_but_last,
vector_int_drop,
vector_int_drop_last,
vector_int_length,
vector_int_reverse,
vector_int_push_all,
vector_int_is_vector_empty,
vector_int_contains,
vector_int_contains_vector_non_contiguous,
vector_int_contains_vector_contiguous,
vector_int_index_of,
vector_int_index_of_vector,
vector_int_occurrences_of,
vector_int_occurrences_of_vector,
vector_int_parse_to_prim,
vector_int_set_nth,
vector_int_split_on,
vector_int_replace,
vector_int_remove,
vector_int_insert,
vector_int_insert_vector,
vector_int_make_empty,
vector_int_sort,
vector_int_sort_reverse,
vector_int_mean,
vector_int_maximum,
vector_int_minimum,
vector_int_sum,
vector_int_mode,
vector_int_two_norm,
vector_int_cumulative_sum,
vector_int_pop,
vector_int_dup,
vector_int_dup_times,
vector_int_swap,
vector_int_rotate,
vector_int_equal,
vector_int_flush,
vector_int_depth,
vector_int_yank,
vector_int_yank_dup,
vector_int_shove,
vector_int_shove_dup,
vector_int_is_empty,
]
}
pub fn vector_float_instructions() -> Vec<fn(&mut PushState)> {
vec![
vector_float_iterate,
vector_float_concat,
vector_float_conj,
vector_float_conj_end,
vector_float_take_n,
vector_float_take_last_n,
vector_float_sub,
vector_float_first,
vector_float_from_first_prim,
vector_float_from_prim,
vector_float_last,
vector_float_from_last_prim,
vector_float_nth,
vector_float_from_nth_prim,
vector_float_rest,
vector_float_but_last,
vector_float_drop,
vector_float_drop_last,
vector_float_length,
vector_float_reverse,
vector_float_push_all,
vector_float_is_vector_empty,
vector_float_contains,
vector_float_contains_vector_non_contiguous,
vector_float_contains_vector_contiguous,
vector_float_index_of,
vector_float_index_of_vector,
vector_float_occurrences_of,
vector_float_occurrences_of_vector,
vector_float_parse_to_prim,
vector_float_set_nth,
vector_float_split_on,
vector_float_replace,
vector_float_remove,
vector_float_insert,
vector_float_insert_vector,
vector_float_make_empty,
vector_float_sort,
vector_float_sort_reverse,
vector_float_mean,
vector_float_maximum,
vector_float_minimum,
vector_float_sum,
vector_float_mode,
vector_float_two_norm,
vector_float_cumulative_sum,
vector_float_pop,
vector_float_dup,
vector_float_dup_times,
vector_float_swap,
vector_float_rotate,
vector_float_equal,
vector_float_flush,
vector_float_depth,
vector_float_yank,
vector_float_yank_dup,
vector_float_shove,
vector_float_shove_dup,
vector_float_is_empty,
]
}
pub fn vector_string_instructions() -> Vec<fn(&mut PushState)> {
vec![
vector_string_iterate,
vector_string_concat,
vector_string_conj,
vector_string_conj_end,
vector_string_take_n,
vector_string_take_last_n,
vector_string_sub,
vector_string_first,
vector_string_from_first_prim,
vector_string_from_prim,
vector_string_last,
vector_string_from_last_prim,
vector_string_nth,
vector_string_from_nth_prim,
vector_string_rest,
vector_string_but_last,
vector_string_drop,
vector_string_drop_last,
vector_string_length,
vector_string_reverse,
vector_string_push_all,
vector_string_is_vector_empty,
vector_string_contains,
vector_string_contains_vector_non_contiguous,
vector_string_contains_vector_contiguous,
vector_string_index_of,
vector_string_index_of_vector,
vector_string_occurrences_of,
vector_string_occurrences_of_vector,
vector_string_parse_to_prim,
vector_string_set_nth,
vector_string_split_on,
vector_string_replace,
vector_string_remove,
vector_string_insert,
vector_string_insert_vector,
vector_string_make_empty,
vector_string_pop,
vector_string_dup,
vector_string_dup_times,
vector_string_swap,
vector_string_rotate,
vector_string_equal,
vector_string_flush,
vector_string_depth,
vector_string_yank,
vector_string_yank_dup,
vector_string_shove,
vector_string_shove_dup,
vector_string_is_empty,
]
}
pub fn vector_boolean_instructions() -> Vec<fn(&mut PushState)> {
vec![
vector_boolean_iterate,
vector_boolean_concat,
vector_boolean_conj,
vector_boolean_conj_end,
vector_boolean_take_n,
vector_boolean_take_last_n,
vector_boolean_sub,
vector_boolean_first,
vector_boolean_from_first_prim,
vector_boolean_from_prim,
vector_boolean_last,
vector_boolean_from_last_prim,
vector_boolean_nth,
vector_boolean_from_nth_prim,
vector_boolean_rest,
vector_boolean_but_last,
vector_boolean_drop,
vector_boolean_drop_last,
vector_boolean_length,
vector_boolean_reverse,
vector_boolean_push_all,
vector_boolean_is_vector_empty,
vector_boolean_contains,
vector_boolean_contains_vector_non_contiguous,
vector_boolean_contains_vector_contiguous,
vector_boolean_index_of,
vector_boolean_index_of_vector,
vector_boolean_occurrences_of,
vector_boolean_occurrences_of_vector,
vector_boolean_parse_to_prim,
vector_boolean_set_nth,
vector_boolean_split_on,
vector_boolean_replace,
vector_boolean_remove,
vector_boolean_insert,
vector_boolean_insert_vector,
vector_boolean_make_empty,
vector_boolean_pop,
vector_boolean_dup,
vector_boolean_dup_times,
vector_boolean_swap,
vector_boolean_rotate,
vector_boolean_equal,
vector_boolean_flush,
vector_boolean_depth,
vector_boolean_yank,
vector_boolean_yank_dup,
vector_boolean_shove,
vector_boolean_shove_dup,
vector_boolean_is_empty,
]
}
pub fn vector_char_instructions() -> Vec<fn(&mut PushState)> {
vec![
vector_char_iterate,
vector_char_concat,
vector_char_conj,
vector_char_conj_end,
vector_char_take_n,
vector_char_take_last_n,
vector_char_sub,
vector_char_first,
vector_char_from_first_prim,
vector_char_from_prim,
vector_char_last,
vector_char_from_last_prim,
vector_char_nth,
vector_char_from_nth_prim,
vector_char_rest,
vector_char_but_last,
vector_char_drop,
vector_char_drop_last,
vector_char_length,
vector_char_reverse,
vector_char_push_all,
vector_char_is_vector_empty,
vector_char_contains,
vector_char_contains_vector_non_contiguous,
vector_char_contains_vector_contiguous,
vector_char_index_of,
vector_char_index_of_vector,
vector_char_occurrences_of,
vector_char_occurrences_of_vector,
vector_char_parse_to_prim,
vector_char_set_nth,
vector_char_split_on,
vector_char_replace,
vector_char_remove,
vector_char_insert,
vector_char_insert_vector,
vector_char_make_empty,
vector_char_pop,
vector_char_dup,
vector_char_dup_times,
vector_char_swap,
vector_char_rotate,
vector_char_equal,
vector_char_flush,
vector_char_depth,
vector_char_yank,
vector_char_yank_dup,
vector_char_shove,
vector_char_shove_dup,
vector_char_is_empty,
]
}
pub fn string_instructions() -> Vec<fn(&mut PushState)> {
vec![
string_iterate,
string_concat,
string_conj,
string_conj_end,
string_take_n,
string_take_last_n,
string_sub,
string_first,
string_from_first_prim,
string_from_prim,
string_last,
string_from_last_prim,
string_nth,
string_from_nth_prim,
string_rest,
string_but_last,
string_drop,
string_drop_last,
string_length,
string_reverse,
string_push_all,
string_is_vector_empty,
string_contains,
string_contains_vector_non_contiguous,
string_contains_vector_contiguous,
string_index_of,
string_index_of_vector,
string_occurrences_of,
string_occurrences_of_vector,
string_parse_to_prim,
string_set_nth,
string_split_on,
string_replace,
string_remove,
string_insert,
string_insert_vector,
string_make_empty,
string_pop,
string_dup,
string_dup_times,
string_swap,
string_rotate,
string_equal,
string_flush,
string_depth,
string_yank,
string_yank_dup,
string_shove,
string_shove_dup,
string_is_empty,
]
}
pub fn code_instructions() -> Vec<fn(&mut PushState)> {
vec![
code_do_then_pop,
code_do_range,
code_do_count,
code_do_times,
code_map,
code_when,
code_is_block,
code_is_singular,
code_length,
code_first,
code_last,
code_rest,
code_but_last,
code_wrap_block,
code_combine,
code_if,
code_member,
code_nth,
code_make_empty_block,
code_is_empty_block,
code_size,
code_extract,
code_insert,
code_first_position,
code_reverse,
code_noop,
code_noop_block,
code_from_int,
code_from_float,
code_from_string,
code_from_boolean,
code_from_char,
code_from_vector_int,
code_from_vector_float,
code_from_vector_string,
code_from_vector_boolean,
code_from_vector_char,
code_pop,
code_from_code,
code_dup,
code_dup_times,
code_swap,
code_rotate,
code_equal,
code_flush,
code_depth,
code_yank,
code_yank_dup,
code_shove,
code_shove_dup,
code_is_empty,
code_from_exec,
]
}
pub fn exec_instructions() -> Vec<fn(&mut PushState)> {
vec![
exec_do_range,
exec_do_count,
exec_do_times,
exec_while,
exec_do_while,
exec_when,
exec_is_block,
exec_is_singular,
exec_length,
exec_first,
exec_last,
exec_rest,
exec_but_last,
exec_wrap_block,
exec_combine,
exec_if,
exec_member,
exec_nth,
exec_make_empty_block,
exec_is_empty_block,
exec_size,
exec_extract,
exec_insert,
exec_first_position,
exec_reverse,
exec_noop,
exec_noop_block,
exec_pop,
exec_dup,
exec_dup_times,
exec_swap,
exec_rotate,
exec_equal,
exec_flush,
exec_depth,
exec_yank,
exec_yank_dup,
exec_shove,
exec_shove_dup,
exec_is_empty,
]
}
pub fn int_instructions() -> Vec<fn(&mut PushState)> {
vec![
int_add,
int_sub,
int_mult,
int_div,
int_rem,
int_max,
int_min,
int_inc,
int_dec,
int_lt,
int_gt,
int_lte,
int_gte,
int_sin,
int_arcsin,
int_cos,
int_arccos,
int_tan,
int_arctan,
int_from_boolean,
int_log,
int_exp,
int_sqrt,
int_inv,
int_abs,
int_sign_reverse,
int_square,
int_from_float,
int_pop,
int_dup,
int_dup_times,
int_swap,
int_rotate,
int_equal,
int_flush,
int_depth,
int_yank,
int_yank_dup,
int_shove,
int_shove_dup,
int_is_empty,
]
}
pub fn float_instructions() -> Vec<fn(&mut PushState)> {
vec![
float_add,
float_sub,
float_mult,
float_div,
float_rem,
float_max,
float_min,
float_inc,
float_dec,
float_lt,
float_gt,
float_lte,
float_gte,
float_sin,
float_arcsin,
float_cos,
float_arccos,
float_tan,
float_arctan,
float_from_boolean,
float_log,
float_exp,
float_sqrt,
float_inv,
float_abs,
float_sign_reverse,
float_square,
float_from_int,
float_pop,
float_dup,
float_dup_times,
float_swap,
float_rotate,
float_equal,
float_flush,
float_depth,
float_yank,
float_yank_dup,
float_shove,
float_shove_dup,
float_is_empty,
]
}
pub fn boolean_instructions() -> Vec<fn(&mut PushState)> {
vec![
boolean_and,
boolean_or,
boolean_not,
boolean_xor,
boolean_invert_first_then_and,
boolean_invert_second_then_and,
boolean_from_int,
boolean_from_float,
boolean_pop,
boolean_dup,
boolean_dup_times,
boolean_swap,
boolean_rotate,
boolean_equal,
boolean_flush,
boolean_depth,
boolean_yank,
boolean_yank_dup,
boolean_shove,
boolean_shove_dup,
boolean_is_empty,
]
}
pub fn char_instructions() -> Vec<fn(&mut PushState)> {
vec![
char_pop,
char_dup,
char_dup_times,
char_swap,
char_rotate,
char_equal,
char_flush,
char_depth,
char_yank,
char_yank_dup,
char_shove,
char_shove_dup,
char_is_empty,
]
}
pub fn all_instructions() -> Vec<fn(&mut PushState)> {
let mut all_vec = vec![];
all_vec.extend(vector_int_instructions().iter());
all_vec.extend(vector_float_instructions().iter());
all_vec.extend(vector_string_instructions().iter());
all_vec.extend(vector_boolean_instructions().iter());
all_vec.extend(vector_char_instructions().iter());
all_vec.extend(string_instructions().iter());
all_vec.extend(code_instructions().iter());
all_vec.extend(exec_instructions().iter());
all_vec.extend(int_instructions().iter());
all_vec.extend(float_instructions().iter());
all_vec.extend(boolean_instructions().iter());
all_vec.extend(char_instructions().iter());
all_vec
}

View File

@ -40,6 +40,7 @@ pub mod macros {
pub mod code; pub mod code;
pub mod common; pub mod common;
pub mod list;
pub mod logical; pub mod logical;
pub mod numeric; pub mod numeric;
pub mod utils; pub mod utils;

View File

@ -219,7 +219,7 @@ fn _make_empty<T>() -> Option<Vec<T>> {
} }
/// Checks if a vector is empty. Pushes true if is, false otherwise /// Checks if a vector is empty. Pushes true if is, false otherwise
fn _is_empty<T>(vals: Vec<T>) -> Option<bool> { fn _is_vector_empty<T>(vals: Vec<T>) -> Option<bool> {
Some(vals.is_empty()) Some(vals.is_empty())
} }
@ -593,7 +593,7 @@ macro_rules! make_vector_instructions {
make_instruction_new!(_reverse, $stack, $stack, $stack); make_instruction_new!(_reverse, $stack, $stack, $stack);
make_instruction_new_aux!(_push_all, $stack, $prim_stack, $stack); make_instruction_new_aux!(_push_all, $stack, $prim_stack, $stack);
// _make_empty would go here // _make_empty would go here
make_instruction_new!(_is_empty, $stack, boolean, $stack); make_instruction_new!(_is_vector_empty, $stack, boolean, $stack);
make_instruction_new!(_contains, $stack, boolean, $stack, $prim_stack); make_instruction_new!(_contains, $stack, boolean, $stack, $prim_stack);
make_instruction_new!( make_instruction_new!(
_contains_vector_non_contiguous, _contains_vector_non_contiguous,
@ -1059,12 +1059,12 @@ mod tests {
let empty_vec: Vec<i128> = vec![]; let empty_vec: Vec<i128> = vec![];
test_state.vector_int = vec![empty_vec.clone()]; test_state.vector_int = vec![empty_vec.clone()];
vector_int_is_empty(&mut test_state); vector_int_is_vector_empty(&mut test_state);
assert_eq!(vec![true], test_state.boolean); assert_eq!(vec![true], test_state.boolean);
test_state.boolean.clear(); test_state.boolean.clear();
test_state.vector_int = vec![vec![1, 2]]; test_state.vector_int = vec![vec![1, 2]];
vector_int_is_empty(&mut test_state); vector_int_is_vector_empty(&mut test_state);
assert_eq!(vec![false], test_state.boolean); assert_eq!(vec![false], test_state.boolean);
} }

View File

@ -1,2 +1,3 @@
pub mod gp;
pub mod instructions; pub mod instructions;
pub mod push; pub mod push;

View File

@ -1,18 +1,32 @@
use crate::instructions::*; use crate::instructions::list::*;
use crate::push::interpreter::interpret_program; use crate::push::interpreter::interpret_program;
use crate::push::state::EMPTY_STATE; use crate::push::state::EMPTY_STATE;
use push::utils::most_genes;
mod instructions; mod instructions;
mod push; mod push;
fn main() { fn main() {
let tvec = vec![1, 2, 3, 4, 5];
println!("{:?}", tvec);
// These need to stay so linter doesn't go crazy. // These need to stay so linter doesn't go crazy.
let mut empty_state = EMPTY_STATE; let mut empty_state = EMPTY_STATE;
empty_state.int = vec![1, 2, 3];
interpret_program(&mut empty_state, 1000, 1000); interpret_program(&mut empty_state, 1000, 1000);
let mut counts: Vec<(&str, usize)> = vec![]; int_instructions();
counts.push(("int", 2)); float_instructions();
counts.push(("float", 1)); string_instructions();
boolean_instructions();
// counts.iter().map() char_instructions();
vector_int_instructions();
vector_float_instructions();
vector_string_instructions();
vector_boolean_instructions();
vector_char_instructions();
code_instructions();
exec_instructions();
all_instructions();
most_genes();
} }

View File

@ -19,10 +19,16 @@ pub fn gene_to_stack(state: &mut PushState, gene: Gene) {
Gene::Block(x) => state.exec.extend(x.into_iter().rev()), Gene::Block(x) => state.exec.extend(x.into_iter().rev()),
Gene::Close => panic!("Close found in the exec stack, this should not happen!"), Gene::Close => panic!("Close found in the exec stack, this should not happen!"),
Gene::Open(_) => panic!("Open found in the exec stack, this should not happen!"), Gene::Open(_) => panic!("Open found in the exec stack, this should not happen!"),
Gene::Skip => panic!("Skip found in the exec stack, this should not happen!"), Gene::Skip => {
state.exec.pop(); // Skip the next item by removing it.
}
Gene::CrossoverPadding => { Gene::CrossoverPadding => {
panic!("CrossoverPadding found in the exec stack, this should not happen!") panic!("CrossoverPadding found in the exec stack, this should not happen!")
} }
Gene::Place(idx) => {
let var = state.input[idx].clone();
state.exec.push(var)
}
} }
} }

View File

@ -1,2 +1,3 @@
pub mod interpreter; pub mod interpreter;
pub mod state; pub mod state;
pub mod utils;

View File

@ -18,6 +18,7 @@ pub struct PushState {
pub vector_char: Vec<Vec<char>>, pub vector_char: Vec<Vec<char>>,
pub exec: Vec<Gene>, pub exec: Vec<Gene>,
pub code: Vec<Gene>, pub code: Vec<Gene>,
pub input: Vec<Gene>,
} }
pub const EMPTY_STATE: PushState = PushState { pub const EMPTY_STATE: PushState = PushState {
@ -33,9 +34,10 @@ pub const EMPTY_STATE: PushState = PushState {
vector_char: vec![], vector_char: vec![],
exec: vec![], exec: vec![],
code: vec![], code: vec![],
input: vec![],
}; };
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone, Hash)]
#[allow(dead_code)] // remove this later. Is here bc Close, Skip, CrossoverPadding #[allow(dead_code)] // remove this later. Is here bc Close, Skip, CrossoverPadding
pub enum Gene { pub enum Gene {
GeneInt(i128), GeneInt(i128),
@ -54,6 +56,7 @@ pub enum Gene {
Skip, Skip,
Block(Vec<Gene>), Block(Vec<Gene>),
CrossoverPadding, CrossoverPadding,
Place(usize),
} }
impl Gene { impl Gene {
@ -129,8 +132,7 @@ impl Gene {
val.insert(n, gene.clone()); val.insert(n, gene.clone());
return true; return true;
} }
match el { if let iblock @ Gene::Block(_) = el {
iblock @ Gene::Block(_) => {
// This line has side effects on iblock if inserts properly. // This line has side effects on iblock if inserts properly.
let success = iblock.attempt_code_insert(gene.clone(), idx - 1); let success = iblock.attempt_code_insert(gene.clone(), idx - 1);
if success { if success {
@ -138,8 +140,6 @@ impl Gene {
} }
idx -= iblock.rec_len() + 1 idx -= iblock.rec_len() + 1
} }
_ => (),
}
idx -= 1; idx -= 1;
} }
if idx == 0 { if idx == 0 {

12
src/push/utils.rs Normal file
View File

@ -0,0 +1,12 @@
use crate::instructions::list::all_instructions;
use crate::push::state::Gene;
pub fn most_genes() -> Vec<Gene> {
let mut instructions: Vec<Gene> = all_instructions()
.into_iter()
.map(|x| Gene::StateFunc(x))
.collect();
instructions.push(Gene::Close);
instructions.push(Gene::Skip);
instructions
}

View File

@ -0,0 +1,95 @@
use polars::prelude::*;
use rush::gp::simplification::auto_simplify_plushy;
use rush::gp::utils::polars_to_gene;
use rush::instructions::numeric::*;
use rush::push::interpreter::interpret_program;
use rush::push::state::Gene;
use rush::push::utils::most_genes;
use rush::{gp::args::PushArgs, push::state::EMPTY_STATE};
use rust_decimal::prelude::FromPrimitive;
use rust_decimal::{Decimal, dec};
/// This is a prototype for an error function. I'm hoping to have some of this
/// refined later.
fn test_error_function(
push_args: &PushArgs,
data: &DataFrame,
push_program: Vec<Gene>,
) -> Vec<Decimal> {
let mut error_vec: Vec<Decimal> = vec![];
let y = data
.column("y")
.unwrap()
.i32()
.unwrap()
.into_iter()
.map(|opt| opt.map(|v| v as i128))
.collect::<Option<Vec<_>>>()
.unwrap(); // How to convert a series to a vector everybody
let x = data.drop("y").unwrap();
// println!("x: {x:#?}");
// println!("y: {y:#?}");
for n in 0..x.height() {
let mut state = EMPTY_STATE;
let mut inputs: Vec<Gene> = Vec::with_capacity(x.width());
let row = x.get_row(n).unwrap();
for datum in row.0.iter() {
inputs.push(polars_to_gene(datum))
}
state.exec.extend(push_program.clone().into_iter()); // load the program
state.input.extend(inputs.clone().into_iter()); // Make inputs available to the state
interpret_program(&mut state, push_args.step_limit, push_args.max_stack_size);
if let Some(top_int) = state.int.pop() {
error_vec.push(Decimal::from_i128((y[n] - top_int).abs()).unwrap());
} else {
error_vec.push(dec!(999999.0)) // super large error if no stack item.
}
}
// println!("{:?}", error_vec);
error_vec
}
#[test]
fn simplification_function_test() {
let train_df: DataFrame = df!(
"x0" => [4, 5, 6],
"x1" => [7, 8, 9],
"y" => [11, 13, 15],
)
.unwrap();
println!("{}", train_df);
// println!("{:#?}", train_df["x0"]);
// push program declaration
let push_program: Vec<Gene> = vec![
Gene::StateFunc(int_inc), // Should get simplified out
Gene::StateFunc(float_tan), // along with all these float instructions
Gene::StateFunc(float_sub),
Gene::StateFunc(int_add), // stays
Gene::StateFunc(float_tan),
Gene::StateFunc(float_sub),
Gene::StateFunc(float_rem),
Gene::StateFunc(float_inc),
Gene::Place(0), // stays
Gene::Place(1), // stays
];
let mut args = PushArgs::new();
args.training_data = Some(train_df.clone());
args.instructions = Some(most_genes());
args.simplification_steps = 100;
args.error_function = Some(test_error_function);
// test_error_function(&args, &train_df, push_program);
// test the auto simplification here
let simplified_genome = auto_simplify_plushy(push_program, args.error_function.unwrap(), args);
assert_eq!(
vec![Gene::StateFunc(int_add), Gene::Place(0), Gene::Place(1)],
simplified_genome
)
}