extern crate crossterm;
extern crate serde;
extern crate serde_json;
use crossterm::style::{
Colorize,
Styler
};
use serde::{Serialize, Deserialize};
use std::io::{
self,
Write
};
use crate::{
base,
bitransform::{
Bitransform,
BitransformDesc,
BitransformRegister
},
boolnet::Boolnet,
evolearn::Parameters
};
mod arch;
mod bitran;
mod dir;
mod evol;
mod help;
mod par;
mod pop;
mod reset;
mod run;
mod save_load;
mod set;
mod stat;
#[derive(Serialize, Deserialize)]
struct Counters {
epoch: usize
}
#[derive(Serialize, Deserialize)]
struct Settings {
log: bool,
test_all: bool,
print_epochs_all: bool
}
pub struct Evolver {
bitran: Box<dyn Bitransform>,
population: Vec<Boolnet>,
parameters: Parameters,
counters: Counters,
settings: Settings
}
const DEF_LOGGING: bool = false;
const DEF_TEST_ALL: bool = false;
const DEF_PRINT_EPOCHS_ALL: bool = false;
const DEF_POP: usize = 250;
const DEF_HEIGHT: usize = 1;
const DEF_SIZE: usize = 48;
const DEF_RANDS: usize = 2;
const DEF_LAYERS: usize = 1;
const DEF_CYCLES: usize = 1;
const DEF_BATCHES: usize = 8;
const DEF_REPLACE_RATIO: f64 = 0.7;
const DEF_PAR_RATIO: f64 = 0.8;
const DEF_PARENTS: usize = 2;
const DEF_PAR_SW_PROB: f64 = 0.9;
const DEF_MUTAT_PROB: f64 = 0.1;
impl Counters {
fn new () -> Self {
let epoch: usize = 0;
Self{epoch}
}
}
impl Settings {
fn new () -> Self {
let log = DEF_LOGGING;
let test_all = DEF_TEST_ALL;
let print_epochs_all = DEF_PRINT_EPOCHS_ALL;
Self{log, test_all, print_epochs_all}
}
}
impl Evolver {
pub fn new_default(register: &BitransformRegister) -> Result<Self, String> {
let bitran = register.new_from_desc(
& BitransformDesc::new("inv-xor", & vec![])
)?;
let mut population = Vec::<Boolnet>::new();
for _ in 0..DEF_POP {
population.push(Boolnet::new_rand(DEF_HEIGHT, DEF_SIZE, DEF_RANDS, DEF_LAYERS, DEF_CYCLES));
}
let parameters = Parameters{
batches: DEF_BATCHES,
replace_ratio: DEF_REPLACE_RATIO,
par_ratio: DEF_PAR_RATIO,
parents: DEF_PARENTS,
par_sw_prob: DEF_PAR_SW_PROB,
mutat_prob: DEF_MUTAT_PROB
};
let counters = Counters::new();
let settings = Settings::new();
Ok(Self{bitran, population, parameters, counters, settings})
}
pub fn shell(&mut self, register: &BitransformRegister) -> Result<(), String> {
let mut hint_shown = false;
loop {
println!("");
println!("{} {:?}", "BITRANSFORM:".blue().bold(), self.bitran);
println!("{} {}", "POPULATION:".blue().bold(), self.population.len());
if self.population.len() > 0 {
let bn = & self.population[0];
println!("{} {} = {}, {} = {}, {} = {}, {} = {}, {} = {}",
"ARCHITECTURE:".blue().bold(),
"height".yellow(), bn.height(),
"size".yellow(), bn.size(),
"rands".yellow(), bn.rands(),
"layers".yellow(), bn.layers(),
"cycles".yellow(), bn.cycles()
);
} else {
println!("{}", "No population, create or load one.".red());
}
let params = & self.parameters;
println!("{} {} = {}, {} = {}, {} = {}, {} = {}, {} = {}, {} = {}",
"PARAMETERS:".blue().bold(),
"batches".yellow(), params.batches,
"replace_ratio".yellow(), params.replace_ratio,
"par_ratio".yellow(), params.par_ratio,
"parents".yellow(), params.parents,
"par_sw_prob".yellow(), params.par_sw_prob,
"mutat_prob".yellow(), params.mutat_prob
);
let settings = & self.settings;
println!("{} {} {}, {} {}, {} {}",
"SETTINGS:".dark_blue().bold(),
"log".dark_yellow(), match settings.log {
false => "off", true => "on"
}.dark_cyan(),
"test".dark_yellow(), match settings.test_all {
false => "new", true => "all"
}.dark_cyan(),
"print".dark_yellow(), match settings.print_epochs_all {
false => "improve", true => "all"
}.dark_cyan()
);
if !hint_shown {
println!("{}", "Type \"?\" to see the list of available commands...".dark_grey());
hint_shown = true;
}
print!("$ ");
let _ = io::stdout().flush();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(..) => {
input.pop();
let tokens: Vec<&str> = input.split_whitespace().collect();
if tokens.len() > 0 {
let command = tokens[0];
match command {
"exit" | "quit" | "end" | "bye" | "q" => {
break;
},
"help" | "?" => {
let comm: &str = if tokens.len() > 1 {
tokens[1]
} else {
""
};
self.help(comm);
},
"stat" => {
match self.stat() {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
},
"evol" | "e" => {
let (parsed, epochs) = if tokens.len() > 1 {
match tokens[1].parse::<usize>() {
Ok(epochs) => (true, epochs),
Err(e) => {
println!("{}", format!("Parse error: arg 1: {}", e.to_string()).red().bold());
(false, 0)
}
}
} else {
(true, 0)
};
if parsed {
match self.evol(epochs) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"par" | "p" => {
let (parsed, param, valstr) = if tokens.len() > 2 {
(true, tokens[1], tokens[2])
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, "", "")
};
if parsed {
match self.par(param, valstr) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"arch" | "a" => {
let (parsed, height, size, rands, layers, cycles) = if tokens.len() > 5 {
match [tokens[1].parse::<usize>(), tokens[2].parse::<usize>(), tokens[3].parse::<usize>(), tokens[4].parse::<usize>(), tokens[5].parse::<usize>()] {
[Ok(height), Ok(size), Ok(rands), Ok(layers), Ok(cycles)] => (true, height, size, rands, layers, cycles),
r => {
(0..5).for_each(|i| if let Err(e) = & r[i] {
println!("{}", format!("Parse error: arg {}: {}", 1 + i, e.to_string()).red().bold());
});
(false, 0, 0, 0, 0, 0)
}
}
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, 0, 0, 0, 0, 0)
};
if parsed {
match self.arch(height, size, rands, layers, cycles) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"pop" => {
let (parsed, size) = if tokens.len() > 1 {
match tokens[1].parse::<usize>() {
Ok(size) => (true, size),
Err(e) => {
println!("{}", format!("Parse error: arg 1: {}", e.to_string()).red().bold());
(false, 0)
}
}
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, 0)
};
if parsed {
match self.pop(size) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"reset" => {
match self.reset() {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
},
"bitran" | "b" => {
let (parsed, id, prefs) = if tokens.len() > 1 {
let mut parsed: bool = true;
let mut prefs = Vec::<usize>::new();
for i in 2..tokens.len() {
match tokens[i].parse::<usize>() {
Ok(pref) => {
prefs.push(pref);
},
Err(e) => {
println!("{}", format!("Parse error: arg {}: {}", i - 1, e.to_string()).red().bold());
parsed = false;
break;
}
}
}
(parsed, tokens[1], prefs)
} else {
(true, "", vec![])
};
if parsed {
match self.bitran(register, id, &prefs) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"run" | "r" => {
let (parsed, bn_ind, inp_hex) = if tokens.len() > 2 {
match tokens[1].parse::<usize>() {
Ok(bn_ind) => (true, bn_ind, tokens[2]),
Err(e) => {
println!("Parse error: arg 1: {}", e.to_string());
(false, 0, "")
}
}
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, 0, "")
};
if parsed {
match self.run(bn_ind, inp_hex) {
Ok(outp_hex) => {
println!("{}", &outp_hex);
},
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
}
}
},
"dir" | "d" => {
let (parsed, dir_inp_hex) = if tokens.len() > 1 {
(true, tokens[1])
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, "")
};
if parsed {
match self.dir(dir_inp_hex) {
Ok(dir_outp_hex) => {
println!("{}", &dir_outp_hex);
},
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
}
}
},
"save" => {
let (parsed, dirpath) = if tokens.len() > 1 {
(true, tokens[1])
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, "")
};
if parsed {
match self.save(dirpath) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"load" => {
let (parsed, dirpath) = if tokens.len() > 1 {
(true, tokens[1])
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, "")
};
if parsed {
match Self::load(register, dirpath) {
Ok(evolver) => {
*self = evolver;
},
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
}
}
}
},
"set" | "s" => {
let (parsed, setting, valstr) = if tokens.len() > 2 {
(true, tokens[1], tokens[2])
} else {
println!("{}", "Parse error: not enough args".red().bold());
(false, "", "")
};
if parsed {
match self.set(setting, valstr) {
Err(s) => {
println!("{}", format!("Error: {}", s).red().bold());
},
_ => {}
}
}
},
"about" => {
base::about();
},
_ => {
println!("{}", "Unknown command".red().bold());
}
}
} else {
println!("{}", "No command, doing nothing".dark_yellow().bold());
}
},
Err(e) => {
println!("{}", format!("Error reading input: {}", e.to_string()).red().bold());
}
}
}
Ok(())
}
}