problem_generator/problem/
codomain.rs

1/*!
2Module for codomain generation, reading, and writing.
3*/
4
5use indicatif::ProgressIterator;
6use rand_chacha::ChaChaRng;
7use structopt::StructOpt;
8use itertools::Itertools;
9
10use super::io::get_output_folder_path_from_configuration_file;
11
12use super::clique_tree::InputParameters;
13use super::codomain_subclasses::*;
14use super::configuration::{ConfigurationParameters, get_rng};
15
16use std::fmt::Write as fmtWrite;
17use std::fs::File;
18use std::io::{BufWriter, Write};
19use std::path::Path;
20use std::path::PathBuf;
21use std::{
22    error::Error,
23    fs::{self, remove_dir_all},
24    str::Lines,
25};
26
27#[derive(StructOpt, Debug)]
28#[structopt(
29    name = "Codomain Generator",
30    about = "Generate the codomain of a TD Mk Landscape using a file or cli arguments"
31)]
32pub struct CodomainOpt {
33    #[structopt(subcommand)]
34    pub codomain_command: CodomainCommand,
35    #[structopt(short = "s", long = "seed")]
36    pub seed: Option<u64>,
37}
38
39#[derive(StructOpt, Debug)]
40pub enum CodomainCommand {
41    /// Generate codomain values for configurations specified in a given file
42    #[structopt(name = "folder")]
43    Folder {
44        ///File to read all the configurations from, for which codomains need to be generated
45        #[structopt(parse(from_os_str))]
46        folder_paths: Vec<PathBuf>,
47    },
48    /// Generate codomain values for configurations specified in a given file
49    #[structopt(name = "file")]
50    File {
51        ///File to read all the configurations from, for which codomains need to be generated
52        #[structopt(parse(from_os_str))]
53        file_path: PathBuf,
54    },
55    /// Generate codomain values for the configuration defined by the cli arguments
56    #[structopt(name = "instance")]
57    Instance {
58        /// The number of subfunctions
59        m: u32,
60        /// The size of the subfunctions
61        k: u32,
62        /// The number of overlapping bits between subfunctions
63        o: u32,
64        /// The branching factor
65        b: u32,
66        /// The output file
67        #[structopt(name = "f", parse(from_os_str))]
68        output_file_path: PathBuf,
69        /// The subfunction to use for the codomain generation
70        #[structopt(subcommand)]
71        codomain_function: CodomainFunction,
72    },
73}
74
75///Run codomain generator from command line options (structopt)
76pub fn run_opt(codomain_opt: CodomainOpt) -> Result<(), Box<dyn Error>> {
77    let mut rng = get_rng(codomain_opt.seed);
78    match codomain_opt.codomain_command {
79        CodomainCommand::Folder { folder_paths} => {
80            for folder_path in folder_paths {
81                handle_folder(folder_path, &mut rng)?;
82            }
83            Ok(())
84        }
85        CodomainCommand::File { file_path } => {
86            handle_input_configuration_file(file_path, &mut rng)
87        },
88        CodomainCommand::Instance {
89            m,
90            k,
91            o,
92            b,
93            output_file_path,
94            codomain_function
95        } => {
96            let input_parameters = InputParameters::new_from_primitives(m, k, o, b);
97            generate_and_write(&input_parameters, &codomain_function, &output_file_path, &mut rng)?;
98            Ok(())
99        }
100    }
101}
102
103///Handle codomain generation for a folder: for every entry in it that is not a folder, pass the file to handle_input_file
104fn handle_folder(folder_path: PathBuf, rng: &mut ChaChaRng) -> Result<(), Box<dyn Error>> {
105    //First we remove all folders that are not named codomain_generation
106    folder_path
107        .read_dir()?
108        .map(|file| file.unwrap())
109        .filter(|file| {
110            file.file_type().unwrap().is_dir() && file.file_name() != "codomain_generation"
111        })
112        .map(|file| remove_dir_all(file.path()))
113        .collect::<Result<Vec<()>, std::io::Error>>()?;
114
115    //Then we read every codomain generation file from the codomain_generation folder
116    let mut codomain_generation_folder_path = folder_path;
117    codomain_generation_folder_path.push("codomain_generation");
118    let file_entries: Vec<PathBuf> = codomain_generation_folder_path
119        .read_dir()?
120        .map(|file| file.unwrap())
121        .filter(|file| !file.file_type().unwrap().is_dir())
122        .map(|file| file.path())
123        .sorted()
124        .collect();
125
126    //And handle each of them
127    file_entries.into_iter().progress().for_each(|path| {
128        handle_input_configuration_file(path, rng).unwrap();
129    });
130
131    Ok(())
132}
133
134///Generate codomain from an input file (path), by reading the parameters from it,
135/// getting the output directory path from the filename and generating the codomain 25 times for all input parameters.
136fn handle_input_configuration_file(
137    input_configuration_file_path: PathBuf,
138    rng: &mut ChaChaRng
139) -> Result<(), Box<dyn Error>> {
140    let experiment_parameters = ConfigurationParameters::from_file(&input_configuration_file_path)?;
141    let codomain_function = experiment_parameters.codomain_function.clone();
142    let directory_path_buf = get_output_folder_path_from_configuration_file(
143        &input_configuration_file_path,
144        "codomain_files",
145    )?;
146
147    //Loop over all input parameters (using custom iterator)
148    for input_parameters in experiment_parameters {
149        //Generate 25 different codomain instances for each input parameter configuration
150        for num in 0..25 {
151            let mut output_file_path = directory_path_buf.clone();
152            let output_file_name = format!(
153                "{}_{}_{}_{}_{}_{}.txt",
154                codomain_function.to_io_string(),
155                input_parameters.m,
156                input_parameters.k,
157                input_parameters.o,
158                input_parameters.b,
159                num
160            );
161
162            output_file_path.push(output_file_name);
163            //println!("constructed output file path: {:?}", output_file_path);
164
165            generate_and_write(&input_parameters, &codomain_function, &output_file_path, rng)?;
166        }
167    }
168
169    Ok(())
170}
171
172///Generate the codomain and write them to the file
173fn generate_and_write(
174    input_parameters: &InputParameters,
175    codomain_function: &CodomainFunction,
176    output_file_path: &Path,
177    rng: &mut ChaChaRng
178) -> Result<(), Box<dyn Error>> {
179    write_codomain(
180        input_parameters,
181        codomain_function,
182        output_file_path,
183        &generate_codomain(input_parameters, codomain_function, rng),
184    )?;
185    Ok(())
186}
187
188///Generate the codomain, write them to the file, and return the codomain values
189pub fn generate_write_return(
190    input_parameters: &InputParameters,
191    codomain_function: &CodomainFunction,
192    output_file_path: &Path,
193    rng: &mut ChaChaRng
194) -> Result<Vec<Vec<f64>>, Box<dyn Error>> {
195    let codomain = generate_codomain(input_parameters, codomain_function, rng);
196    write_codomain(
197        input_parameters,
198        codomain_function,
199        output_file_path,
200        &codomain,
201    )?;
202    Ok(codomain)
203}
204
205///Generate the codomain, by calling the right generation function
206pub fn generate_codomain(
207    input_parameters: &InputParameters,
208    codomain_function: &CodomainFunction,
209    rng: &mut ChaChaRng
210) -> Vec<Vec<f64>> {
211    match codomain_function {
212        CodomainFunction::Random => generate_random(input_parameters, rng),
213        CodomainFunction::Trap => generate_trap(input_parameters, 2.5),
214        CodomainFunction::DeceptiveTrap => generate_trap_general(input_parameters, rng), // generate_trap(input_parameters, 1.0),
215        CodomainFunction::NKq { q } => generate_nk_q(input_parameters, *q, rng),
216        CodomainFunction::NKp { p } => generate_nk_p(input_parameters, *p, rng),
217        CodomainFunction::RandomDeceptiveTrap { p_deceptive } => {
218            generate_random_trap(input_parameters, *p_deceptive, rng)
219        }
220        CodomainFunction::Unknown => panic!("We can't generate codomain for unknown codomain"),
221    }
222}
223
224///Write the codomain to the passed file
225fn write_codomain(
226    input_parameters: &InputParameters,
227    codomain_function: &CodomainFunction,
228    file_path: &Path,
229    codomain: &[Vec<f64>],
230) -> Result<(), Box<dyn Error>> {
231    let file = File::create(file_path)?;
232    let mut buf_writer = BufWriter::new(file);
233    let mut write_buffer = String::new();
234
235    //Write the codomain function on the first line
236    writeln!(write_buffer, "{}", codomain_function)?;
237    buf_writer.write_all(write_buffer.as_bytes())?;
238    write_buffer.clear();
239
240    //Write the input parameters on the second line
241    writeln!(
242        write_buffer,
243        "{} {} {} {}",
244        input_parameters.m, input_parameters.k, input_parameters.o, input_parameters.b
245    )?;
246    buf_writer.write_all(write_buffer.as_bytes())?;
247    write_buffer.clear();
248
249    //Write all codomain values on the subsequent lines
250    for clique in codomain {
251        for value in clique {
252            writeln!(write_buffer, "{}", value)?;
253            buf_writer.write_all(write_buffer.as_bytes())?;
254            write_buffer.clear();
255        }
256    }
257
258    //Flush all data still in the buffer
259    buf_writer.flush()?;
260
261    Ok(())
262}
263
264///Get the codomain values from a file's content iterator
265/// First skip a given number of lines and then read all the values
266pub fn get_codomain_from_iterator(
267    content_iterator: &mut Lines,
268    skip_number_lines: u32,
269    input_parameters: &InputParameters,
270) -> Result<Vec<Vec<f64>>, Box<dyn Error>> {
271    let mut content_iterator = content_iterator.skip(skip_number_lines as usize);
272    let mut codomain = Vec::with_capacity(input_parameters.m as usize);
273    for _i in 0..(input_parameters.m as usize) {
274        let mut clique_codomain = Vec::with_capacity((1 << input_parameters.k) as usize);
275        for _j in 0..(1 << input_parameters.k) {
276            let fitness: f64 = content_iterator
277                .next()
278                .ok_or("Codomain file does not contain enough entries")?
279                .parse()?;
280            clique_codomain.push(fitness);
281        }
282        codomain.push(clique_codomain);
283    }
284
285    Ok(codomain)
286}
287
288///Read the codomain values from a file at the given path
289pub fn read_codomain(
290    input_parameters: &InputParameters,
291    codomain_file: &Path,
292    skip_number_lines: u32,
293) -> Result<Vec<Vec<f64>>, Box<dyn Error>> {
294    let contents = fs::read_to_string(codomain_file)?;
295    //println!("contents of file: {}", contents);
296    let mut content_iterator = contents.lines();
297    get_codomain_from_iterator(&mut content_iterator, skip_number_lines, input_parameters)
298}