ahc_evaluation/evaluation/
record.rs

1use std::path::Path;
2
3use anyhow::{ensure, Context};
4use itertools::Itertools;
5
6#[derive(Debug, Clone, Copy, serde::Serialize)]
7pub struct EvaluationRecord {
8    pub seed: usize,
9    pub score: i64,
10    pub execution_time: f64,
11}
12
13/// Shows statistics about scores and execution times.
14pub fn show_statistics(evaluation_table: &[EvaluationRecord]) -> anyhow::Result<()> {
15    show_score_statistics(evaluation_table)?;
16    println!();
17    show_execution_time_statistics(evaluation_table)?;
18
19    Ok(())
20}
21
22/// Shows score statistics.
23fn show_score_statistics(evaluation_table: &[EvaluationRecord]) -> anyhow::Result<()> {
24    ensure!(
25        !evaluation_table.is_empty(),
26        "The evaluation table is empty."
27    );
28
29    let total_score = evaluation_table
30        .iter()
31        .map(|record| record.score)
32        .sum::<i64>();
33    let avg_score = total_score as f64 / evaluation_table.len() as f64;
34
35    let (min_pos, max_pos) = match evaluation_table
36        .iter()
37        .position_minmax_by_key(|record| record.score)
38    {
39        itertools::MinMaxResult::NoElements => unreachable!(),
40        itertools::MinMaxResult::OneElement(pos) => (pos, pos),
41        itertools::MinMaxResult::MinMax(min_pos, max_pos) => (min_pos, max_pos),
42    };
43
44    let min_record = evaluation_table[min_pos];
45    let max_record = evaluation_table[max_pos];
46
47    print!(
48        "\
49[Score Statistics]
50Total: {}
51Average: {:.3}
52Min: {} (seed = {})
53Max: {} (seed = {})
54",
55        total_score,
56        avg_score,
57        min_record.score,
58        min_record.seed,
59        max_record.score,
60        max_record.seed,
61    );
62
63    Ok(())
64}
65
66/// Shows execution time statistics.
67fn show_execution_time_statistics(evaluation_table: &[EvaluationRecord]) -> anyhow::Result<()> {
68    ensure!(
69        !evaluation_table.is_empty(),
70        "The evaluation table is empty."
71    );
72
73    let total_exe_time = evaluation_table
74        .iter()
75        .map(|record| record.execution_time)
76        .sum::<f64>();
77    let avg_exe_time = total_exe_time / evaluation_table.len() as f64;
78
79    let (min_pos, max_pos) = match evaluation_table
80        .iter()
81        .position_minmax_by(|x, y| x.execution_time.partial_cmp(&y.execution_time).unwrap())
82    {
83        itertools::MinMaxResult::NoElements => unreachable!(),
84        itertools::MinMaxResult::OneElement(pos) => (pos, pos),
85        itertools::MinMaxResult::MinMax(min_pos, max_pos) => (min_pos, max_pos),
86    };
87
88    let min_record = evaluation_table[min_pos];
89    let max_record = evaluation_table[max_pos];
90
91    print!(
92        "\
93[Execution Time]
94Total: {:.3}
95Average: {:.3}
96Min: {:.3} (seed = {})
97Max: {:.3} (seed = {})
98",
99        total_exe_time,
100        avg_exe_time,
101        min_record.execution_time,
102        min_record.seed,
103        max_record.execution_time,
104        max_record.seed,
105    );
106
107    Ok(())
108}
109
110/// Outputs score and execution time record per seed to CSV file.
111pub fn write_to_csv<P>(
112    output_file_path: P,
113    evaluation_table: &[EvaluationRecord],
114) -> anyhow::Result<()>
115where
116    P: AsRef<Path>,
117{
118    let mut writer = csv::Writer::from_path(&output_file_path)
119        .with_context(|| "Failed to open file to output evaluation table.")?;
120
121    for record in evaluation_table {
122        writer
123            .serialize(record)
124            .with_context(|| "Failed to serialize the evaluation record.")?;
125    }
126
127    writer
128        .flush()
129        .with_context(|| "Failed to write evaluation table to file.")?;
130
131    Ok(())
132}