added benchmark

This commit is contained in:
WanderingPenwing 2024-12-04 17:57:03 +01:00
parent 2f3555b303
commit 9dd739ffcc
5 changed files with 140 additions and 22 deletions

View file

@ -21,6 +21,10 @@ struct Args {
#[arg(long)]
limit: Option<usize>, // Optional value
/// Solve n times and averages the results
#[arg(long)]
benchmark: Option<usize>, // Optional value
/// Disable randomization of the collapse
#[arg(long)]
norand: bool,
@ -45,6 +49,10 @@ fn main() {
solver.random_mode(!args.norand);
solver.grid_display_mode(args.grid);
if let Some(number) = args.benchmark {
solver.benchmark(number);
return
}
if let Err(reason) = solver.solve(args.limit) {
println!("{}", reason);
if args.debug {

View file

@ -57,10 +57,6 @@ impl Cell {
}
}
pub fn get_state(&self) -> Option<usize> {
self.state
}
pub fn get_allowed(&self) -> Vec<usize> {
self.blocking_states
.iter()

View file

@ -1,5 +1,7 @@
use std::collections::HashSet;
use std::time::Instant;
use std::time::Duration;
use std::ops::MulAssign;
use std::fmt;
pub mod sudoku;
@ -14,7 +16,7 @@ pub enum WaveError {
impl fmt::Display for WaveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WaveError::Contradiction => write!(f, "Error: The puzzle contradicts itself."),
WaveError::Contradiction => write!(f, "Error: There is a contradiction in the puzzle."),
WaveError::NoHistory => write!(f, "Error: The puzzle is impossible (backtracked to start)."),
}
}
@ -33,6 +35,77 @@ impl fmt::Display for Step {
}
}
pub struct SolveResult {
total_time: Duration,
propagation: (usize, Duration),
collapse: (usize, Duration),
backtrack: (usize, Duration),
}
impl fmt::Display for SolveResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let propagation_percentage = (self.propagation.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
let collapse_percentage = (self.collapse.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
let backtrack_percentage = (self.backtrack.1.as_secs_f64() / self.total_time.as_secs_f64()) * 100.0;
write!(f, "# total time : {:.2?} ({:.2?}% propagation, {:.2?}% forced collapse, {:.2?}% backtrack)\n- {} propagations ({:.2?}), {} forced collapse ({:.2?}), {} backtrack ({:.2?})",
self.total_time, propagation_percentage, collapse_percentage, backtrack_percentage,
self.propagation.0, (self.propagation.1.checked_div(self.propagation.0 as u32)).unwrap_or(std::time::Duration::ZERO),
self.collapse.0, (self.collapse.1.checked_div(self.collapse.0 as u32)).unwrap_or(std::time::Duration::ZERO),
self.backtrack.0, (self.backtrack.1.checked_div(self.backtrack.0 as u32)).unwrap_or(std::time::Duration::ZERO)
)
}
}
impl std::ops::AddAssign for SolveResult {
fn add_assign(&mut self, other: Self) {
self.total_time += other.total_time;
self.propagation.0 += other.propagation.0;
self.propagation.1 += other.propagation.1;
self.collapse.0 += other.collapse.0;
self.collapse.1 += other.collapse.1;
self.backtrack.0 += other.backtrack.0;
self.backtrack.1 += other.backtrack.1;
}
}
impl MulAssign<f32> for SolveResult {
fn mul_assign(&mut self, factor: f32) {
// Scale the durations
self.total_time = scale_duration(self.total_time, factor);
self.propagation.1 = scale_duration(self.propagation.1, factor);
self.collapse.1 = scale_duration(self.collapse.1, factor);
self.backtrack.1 = scale_duration(self.backtrack.1, factor);
// Scale the usize counts (rounded to nearest integer)
self.propagation.0 = scale_usize(self.propagation.0, factor);
self.collapse.0 = scale_usize(self.collapse.0, factor);
self.backtrack.0 = scale_usize(self.backtrack.0, factor);
}
}
impl Default for SolveResult {
fn default() -> Self {
Self {
total_time: Duration::ZERO,
propagation: (0, Duration::ZERO),
collapse: (0, Duration::ZERO),
backtrack: (0, Duration::ZERO),
}
}
}
// Helper to scale `Duration` by `f32`
fn scale_duration(duration: Duration, factor: f32) -> Duration {
let nanos = duration.as_secs_f64() * (factor as f64);
Duration::from_secs_f64(nanos)
}
// Helper to scale `usize` by `f32`
fn scale_usize(value: usize, factor: f32) -> usize {
((value as f64) * (factor as f64)).round() as usize
}
pub struct Solver {
grid: Vec<Vec<cell::Cell>>,
history: Vec<Step>,
@ -41,6 +114,7 @@ pub struct Solver {
square_size: usize,
debug_display: bool,
grid_display: bool,
benchmarking: bool,
collapse_option: cell::CollapseOption,
}
@ -57,10 +131,19 @@ impl Solver {
square_size: order,
debug_display: false,
grid_display: false,
benchmarking: false,
collapse_option: cell::CollapseOption::Random,
}
}
fn reset_grid(&mut self) {
let states = (1..=self.size).collect();
let empty_grid = vec![vec![cell::Cell::new(states); self.size]; self.size];
self.grid = empty_grid.clone();
self.history = vec![];
self.last_move_index = 0;
}
pub fn random_mode(&mut self, collapse_random: bool) {
if collapse_random {
self.collapse_option = cell::CollapseOption::Random;
@ -69,14 +152,35 @@ impl Solver {
}
}
pub fn solve(&mut self, solver_limit: Option<usize>) -> Result<(), WaveError> {
pub fn benchmark(&mut self, solve_number: usize) {
self.benchmarking = true;
let mut results_sum: SolveResult = SolveResult::default();
let mut error_number: usize = 0;
for _ in 0..solve_number {
println!();
self.reset_grid();
match self.solve(None) {
Ok(result) => {
results_sum += result;
}
Err(reason) => {
println!("{}", reason);
error_number += 1;
}
}
}
results_sum *= 1.0 / ((solve_number - error_number) as f32);
println!("\n---------\n{}\n\n{} errors", results_sum, error_number);
}
pub fn solve(&mut self, solver_limit: Option<usize>) -> Result<SolveResult, WaveError> {
let start_time = Instant::now();
self.display(ui::DisplayMode::Full);
let mut propagation_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
let mut collapse_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
let mut backtrack_counter: (usize, std::time::Duration) = (0, std::time::Duration::ZERO);
println!("# started");
self.debug("--------");
while self.history.len() < self.size * self.size {
@ -129,23 +233,18 @@ impl Solver {
}
}
self.debug("--------");
let total_elapsed = start_time.elapsed();
let propagation_percentage = (propagation_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
let collapse_percentage = (collapse_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
let backtrack_percentage = (backtrack_counter.1.as_secs_f64() / total_elapsed.as_secs_f64()) * 100.0;
println!("\n# finished in {:.2?} ({:.2?}% propagation, {:.2?}% forced collapse, {:.2?}% backtrack)",
total_elapsed, propagation_percentage, collapse_percentage, backtrack_percentage
);
println!("- {} propagations ({:.2?}), {} forced collapse ({:.2?}), {} backtrack ({:.2?})",
propagation_counter.0, (propagation_counter.1.checked_div(propagation_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO),
collapse_counter.0, (collapse_counter.1.checked_div(collapse_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO),
backtrack_counter.0, (backtrack_counter.1.checked_div(backtrack_counter.0 as u32)).unwrap_or(std::time::Duration::ZERO)
);
if !self.grid_display || self.debug_display {
self.display(ui::DisplayMode::Full);
}
Ok(())
let total_time = start_time.elapsed();
let result = SolveResult {
total_time,
propagation : propagation_counter,
collapse : collapse_counter,
backtrack : backtrack_counter,
};
println!("\n{}", result);
Ok(result)
}
fn collapse(&mut self) -> Result<(), WaveError> {

View file

@ -21,6 +21,9 @@ pub enum DisplayMode {
impl Solver {
pub fn display(&self, display_mode: DisplayMode) {
if self.benchmarking {
return
}
if let DisplayMode::Erase = display_mode {
let height = self.size + self.square_size + 2;
print!("\x1b[{}A", height);

12
todo Normal file
View file

@ -0,0 +1,12 @@
benchmark 3 (n = 3000)
# total time : 10.05ms (62.03% propagation, 37.31% forced collapse, 0.12% backtrack)
- 78 propagations (79.96µs), 48 forced collapse (78.16µs), 0 backtrack (0.00ns)
benchmark 4 (n = 300)
# total time : 116.36ms (43.53% propagation, 56.03% forced collapse, 0.23% backtrack)
- 265 propagations (191.13µs), 183 forced collapse (356.23µs), 4 backtrack (66.91µs)
benchmark 5 (n = 100)
# total time : 886.94ms (31.81% propagation, 67.66% forced collapse, 0.43% backtrack)
- 743 propagations (379.77µs), 506 forced collapse (1.19ms), 27 backtrack (140.68µs)