From 155ed84a2eb0d8ad439e9d1065f1ccbd41c52d4d Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Thu, 23 Nov 2023 12:04:45 +0100 Subject: [PATCH 01/32] idea --- rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index 0c482e81..da96d28d 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -66,6 +66,7 @@ //! However I don't see any improvement of the prediction accuracy. //! //! Future experiments: +//! * Use confusion matrix for determining what parameters have impact on the precision. And enable/disable features based on the confusion matrix. //! * Transform the `train` pairs: rotate90, rotate180, rotate270, flipx, flipy. //! * Transform the `test` pairs: rotate90, rotate180, rotate270, flipx, flipy. //! * Provide `weight` to logistic regression, depending on how important each parameter is. From 48a5fe6737e2efb0d426797f409dc8d2d878da62 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 11:18:56 +0100 Subject: [PATCH 02/32] enabled loda-rust-arc --- rust_project/loda-rust-cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/Cargo.toml b/rust_project/loda-rust-cli/Cargo.toml index 7331e6f2..07f70843 100644 --- a/rust_project/loda-rust-cli/Cargo.toml +++ b/rust_project/loda-rust-cli/Cargo.toml @@ -12,7 +12,7 @@ name = "loda-rust" path = "src/main.rs" [features] -# default = ["loda-rust-arc"] +default = ["loda-rust-arc"] loda-rust-arc = ["dep:petgraph", "dep:image_crate", "dep:linfa", "dep:linfa-logistic", "dep:linfa-preprocessing", "dep:ndarray", "dep:rayon"] [dependencies] From 5896295b229253fbca6f00bb091aebfca74270e8 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 13:14:58 +0100 Subject: [PATCH 03/32] ideas --- rust_project/loda-rust-cli/src/arc/label.rs | 1 + rust_project/loda-rust-cli/src/arc/solve_split.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/src/arc/label.rs b/rust_project/loda-rust-cli/src/arc/label.rs index 334199be..b8a583a7 100644 --- a/rust_project/loda-rust-cli/src/arc/label.rs +++ b/rust_project/loda-rust-cli/src/arc/label.rs @@ -468,6 +468,7 @@ pub enum ActionLabel { // ObjectsOnlyMoveInDirectionY, // CropWhenFinishedDrawingInsideSingleColorObjectWithColor { color: u8 }, // OutputImageCropOutSingleColorObject { color: u8 }, + // OutputSizeIsHalfOfInputSizeDependingOnInputOrientation, // OutputSizeIsTheSameAsBoundingBoxOfSingleColorObject { color: u8 }, // OutputSizeIsTheSameAsHoleInSingleColorObject { color: u8 }, // OutputSizeIsTheSameAsInputSmallestSingleColorObjectRectangle, diff --git a/rust_project/loda-rust-cli/src/arc/solve_split.rs b/rust_project/loda-rust-cli/src/arc/solve_split.rs index 8eee0e8e..fe540aaf 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_split.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_split.rs @@ -12,6 +12,8 @@ //! e98196ab, f2829549, fafffa47 //! //! Known problem: Can only split into columns or rows, not both. +//! If a task contains both horizontal and vertical splits, then it cannot determine which split to use. Here a mechanism for determining what split to use is needed. +//! If a task is a grid, then it cannot extract the grid cells. //! //! Known problem: Returns a result the moment it finds something that seems to be a solution. //! It doesn't attempt to find a better solution. @@ -31,7 +33,7 @@ //! * Return multiple predictions, up to 3 is allowed. //! //! Hard splitview tasks that it currently cannot solve, and will require more advanced techniques: -//! 136b0064, 351d6448, 93b4f4b3, d47aa2ff, e3497940 +//! 136b0064, 351d6448, 93b4f4b3, d47aa2ff, e3497940, e6721834 use super::arc_work_model::{Task, Input, PairType}; use super::{ImageLabel, SplitLabel, ImageSplit, ImageSplitDirection, ImageOverlay, ImageHistogram, ColorMap, ImageSize}; use super::{Image, ImageMaskBoolean, Histogram, ImageReplaceColor, ImageSymmetry}; From cc649336d3b0537481cc3449012c1f3a502e403d Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 13:15:11 +0100 Subject: [PATCH 04/32] dead code --- rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index eef06e29..d37cc5fa 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -398,12 +398,10 @@ struct ProcessedTask { } pub struct SolveLogisticRegression { - #[allow(dead_code)] tasks: Vec, } impl SolveLogisticRegression { - #[allow(dead_code)] pub fn new(tasks: Vec) -> Self { // println!("loaded {} tasks", tasks.len()); Self { From 409da39d52bb606f8abbc36d9cb24b0e682c5659 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 13:15:31 +0100 Subject: [PATCH 05/32] SolveOneColor added --- rust_project/loda-rust-cli/src/arc/mod.rs | 2 + .../loda-rust-cli/src/arc/solve_one_color.rs | 428 ++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 rust_project/loda-rust-cli/src/arc/solve_one_color.rs diff --git a/rust_project/loda-rust-cli/src/arc/mod.rs b/rust_project/loda-rust-cli/src/arc/mod.rs index c504a3b5..fd901be0 100644 --- a/rust_project/loda-rust-cli/src/arc/mod.rs +++ b/rust_project/loda-rust-cli/src/arc/mod.rs @@ -148,6 +148,7 @@ mod single_color_object; mod single_color_object_satisfies_label; mod single_color_object_to_label; mod solve_logisticregression; +mod solve_one_color; mod solve_split; mod split; mod split_to_label; @@ -291,6 +292,7 @@ pub use single_color_object::{SingleColorObject, SingleColorObjectRectangle, Sin pub use single_color_object_satisfies_label::SingleColorObjectSatisfiesLabel; pub use single_color_object_to_label::SingleColorObjectToLabel; pub use solve_logisticregression::SolveLogisticRegression; +pub use solve_one_color::SolveOneColor; pub use solve_split::{Operation, SolveSplit, SolveSplitFoundSolution}; pub use split::{EvenSplit, Split, SplitCandidate, SplitCandidateContainer}; pub use split_to_label::SplitToLabel; diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs new file mode 100644 index 00000000..9e64eb8d --- /dev/null +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -0,0 +1,428 @@ +//! Solve tasks that outputs a single color. +//! +//! Example: +//! d631b094 +use super::arc_json_model::GridFromImage; +use super::arc_work_model::{Task, PairType, Pair}; +use super::{Image, ImageOverlay, arcathon_solution_coordinator, arc_json_model, ImageMix, MixMode, ObjectsAndMass, ImageCrop, Rectangle, ImageExtractRowColumn, ImageDenoise, TaskGraph, ShapeType, ImageSize, ShapeTransformation, SingleColorObject, ShapeIdentificationFromSingleColorObject, ImageDetectHole, ImagePadding, ImageRepairPattern, TaskNameToPredictionVec, CreateTaskWithSameSize, ImageReplaceColor, ImageCenterIndicator, ImageGravity, GravityDirection, DiagonalHistogram, RecordTrigram, ImageNgram, ImageExteriorCorners, LargestInteriorRectangle, ImageDrawRect, PropertyOutput, ImageProperty, ImageResize, ImageRepeat, rule, CellularAutomaton, ChangeItem}; +use super::{ActionLabel, ImageLabel, ImageMaskDistance, LineSpan, LineSpanDirection, LineSpanMode, VerifyPrediction, VerifyPredictionWithTask}; +use super::{HtmlLog, PixelConnectivity, ImageHistogram, Histogram, ImageEdge, ImageMask}; +use super::{ImageNeighbour, ImageNeighbourDirection, ImageCornerAnalyze, ImageMaskGrow, Shape3x3}; +use super::human_readable_utc_timestamp; +use anyhow::{Context, bail}; +use indicatif::{ProgressBar, ProgressStyle}; +use serde::Serialize; +use std::borrow::BorrowMut; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, Mutex}; +use rayon::prelude::*; +use std::sync::atomic::{AtomicUsize, Ordering}; + +const PROCESS_TASK_VARIANTS: [u8; 1] = [0]; + +#[derive(Clone, Debug)] +enum ProcessTaskMode { + InputOutputSameSize, + InputOutputDifferentSize, +} + +#[derive(Clone, Debug)] +pub struct ProcessTaskContext { + variant: u8, + mode: ProcessTaskMode, + input_size_vec: Vec, + output_size_vec: Vec, + scale_widthheight: Option<(u8, u8)>, +} + +impl ProcessTaskContext { + pub fn new(task: &Task, variant: u8) -> Self { + let mode: ProcessTaskMode = if task.is_output_size_same_as_input_size() { + ProcessTaskMode::InputOutputSameSize + } else { + ProcessTaskMode::InputOutputDifferentSize + }; + let mut instance = Self { + variant, + mode, + input_size_vec: Vec::::new(), + output_size_vec: Vec::::new(), + scale_widthheight: None, + }; + instance.populate_input_size_vec(task); + instance.populate_output_size_vec(task); + instance.populate_scale_factor(task); + instance + } + + fn populate_input_size_vec(&mut self, task: &Task) { + self.input_size_vec.clear(); + for pair in &task.pairs { + let size: ImageSize = pair.input.image.size(); + self.input_size_vec.push(size); + } + } + + fn populate_output_size_vec(&mut self, task: &Task) { + self.output_size_vec.clear(); + for pair in &task.pairs { + match pair.pair_type { + PairType::Train => { + let size: ImageSize = pair.output.image.size(); + self.output_size_vec.push(size); + }, + PairType::Test => { + let mut the_size: ImageSize = pair.output.test_image.size(); + if let Some(size) = pair.predicted_output_size() { + the_size = size; + } + self.output_size_vec.push(the_size); + } + } + } + } + + fn populate_scale_factor(&mut self, task: &Task) { + let mut scale_width_factor: Option = None; + let mut scale_height_factor: Option = None; + for action_label in &task.action_label_set_intersection { + match action_label { + ActionLabel::OutputPropertyIsInputPropertyMultipliedBy { output, input, scale } => { + match (output, input) { + (PropertyOutput::OutputWidth, ImageProperty::Width) => { + scale_width_factor = Some(*scale); + }, + (PropertyOutput::OutputHeight, ImageProperty::Height) => { + scale_height_factor = Some(*scale); + }, + _ => {} + } + }, + ActionLabel::OutputPropertyIsEqualToInputProperty { output, input } => { + match (output, input) { + (PropertyOutput::OutputWidth, ImageProperty::Width) => { + scale_width_factor = Some(1); + }, + (PropertyOutput::OutputHeight, ImageProperty::Height) => { + scale_height_factor = Some(1); + }, + _ => {} + } + }, + _ => {} + } + } + if let Some(scale_x) = scale_width_factor { + if let Some(scale_y) = scale_height_factor { + if scale_x > 0 && scale_y > 0 && scale_x <= 6 && scale_y <= 6 { + if scale_x != 1 && scale_y != 1 { + self.scale_widthheight = Some((scale_x, scale_y)); + } + } + } + } + } +} + +pub struct SolveOneColor { + tasks: Vec, +} + +impl SolveOneColor { + pub fn new(tasks: Vec) -> Self { + // println!("loaded {} tasks", tasks.len()); + Self { + tasks, + } + } + + /// Checks that the predicted output is the same as the expected output. + /// + /// This can be run with the public ARC dataset contains expected output for the test pairs. + /// + /// This cannot be run with the hidden ARC dataset, which doesn't contain expected output for the test pairs. + pub fn run_and_verify(&self) -> anyhow::Result<()> { + let run_and_verify_htmllog = true; + let number_of_tasks: u64 = self.tasks.len() as u64; + println!("{} - run start - will process {} tasks with SolveOneColor", human_readable_utc_timestamp(), number_of_tasks); + let count_solved_full = AtomicUsize::new(0); + let count_solved_partial = AtomicUsize::new(0); + let pb = ProgressBar::new(number_of_tasks as u64); + pb.set_style(ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) {msg}")? + .progress_chars("#>-") + ); + + self.tasks.par_iter().for_each(|task| { + pb.inc(1); + + // Only process tasks where all pairs agree that the output images have just one color. + if !Self::all_pairs_have_one_output_color(task) { + return; + } + HtmlLog::text(format!("task {}", task.id)); + + // Only process tasks where the task has a predicted size. + // if the task doesn't have a predicted size, then skip it. + + let task_count_test: usize = task.count_test(); + + // Make predictions + let processed_task: ProcessedTask = match Self::process_task(task) { + Ok(value) => value, + Err(error) => { + pb.println(format!("task {} - error: {:?}", task.id, error)); + return; + } + }; + + // Verify predictions + let mut correct_vec = Vec::::new(); + let mut test_index_to_correct_count = HashMap::::new(); + for prediction in &processed_task.prediction_vec { + let mut is_correct = false; + match prediction.verify_prediction(task) { + Ok(verify_prediction) => { + match verify_prediction { + VerifyPrediction::Correct => { + is_correct = true; + + // Count the number of times each test pair is solved correctly. + test_index_to_correct_count.entry(prediction.output_id as usize).and_modify(|e| *e += 1).or_insert(1); + }, + _ => {} + } + }, + Err(error) => { + pb.println(format!("task: {} - output_id: {} - verify_prediction - error: {:?}", task.id, prediction.output_id, error)); + } + } + correct_vec.push(is_correct); + } + + let mut fully_solved_test_pairs = true; + let mut number_of_solved_test_pairs: usize = 0; + for i in 0..task_count_test { + let count: usize = match test_index_to_correct_count.get(&i) { + Some(value) => *value, + None => { + fully_solved_test_pairs = false; + continue; + } + }; + if count == 0 { + fully_solved_test_pairs = false; + } + if count >= 1 { + number_of_solved_test_pairs += 1; + } + } + + if fully_solved_test_pairs { + count_solved_full.fetch_add(1, Ordering::Relaxed); + pb.println(format!("task {} - solved full, {} test pairs", task.id, number_of_solved_test_pairs)); + HtmlLog::text(format!("task {} - solved full, {} test pairs", task.id, number_of_solved_test_pairs)); + } else { + if number_of_solved_test_pairs >= 1 { + count_solved_partial.fetch_add(1, Ordering::Relaxed); + pb.println(format!("task {} - solved partial, {} correct of {} test pairs", task.id, number_of_solved_test_pairs, task_count_test)); + HtmlLog::text(format!("task {} - solved partial, {} correct of {} test pairs", task.id, number_of_solved_test_pairs, task_count_test)); + } + } + let count_full: usize = count_solved_full.load(Ordering::Relaxed); + let count_partial: usize = count_solved_partial.load(Ordering::Relaxed); + pb.set_message(format!("Solved full: {}, partial: {}", count_full, count_partial)); + + // Display the internal computed image to the html log + if run_and_verify_htmllog { + for (index, ptwotp) in processed_task.ptwotp_vec.iter().enumerate() { + HtmlLog::compare_images(ptwotp.inspect_internal_image_vec.clone()); + let is_correct: bool = correct_vec[index]; + if is_correct { + HtmlLog::text(format!("{} - test_index: {} - correct", task.id, ptwotp.test_index)); + HtmlLog::image(&ptwotp.cropped_image); + } else { + HtmlLog::text(format!("{} - test_index: {} - incorrect", task.id, ptwotp.test_index)); + let pair: &Pair = match task.pair_for_test_index(ptwotp.test_index) { + Ok(pair) => pair, + Err(error) => { + pb.println(format!("{} - error: {:?}", task.id, error)); + continue; + } + }; + let images: Vec = vec![ + pair.input.image.clone(), + pair.output.test_image.clone(), + ptwotp.cropped_image.clone(), + ]; + HtmlLog::compare_images(images); + } + } + } + }); + pb.finish_and_clear(); + let count_full: usize = count_solved_full.load(Ordering::Relaxed); + let count_partial: usize = count_solved_partial.load(Ordering::Relaxed); + println!("{} - run - end", human_readable_utc_timestamp()); + println!("{} - out of {} tasks, fully solved {} and partially solved {}", human_readable_utc_timestamp(), number_of_tasks, count_full, count_partial); + Ok(()) + } + + fn process_task(task: &Task) -> anyhow::Result { + let mut accumulated_processed_task = ProcessedTask { + ptwotp_vec: vec!(), + prediction_vec: vec!(), + }; + + for variant in &PROCESS_TASK_VARIANTS { + let processed_task: ProcessedTask = Self::process_task_item(task, *variant) + .with_context(|| format!("task: {} Unable to process_task_item() with variant: {}", task.id, variant))?; + + accumulated_processed_task.ptwotp_vec.extend(processed_task.ptwotp_vec); + accumulated_processed_task.prediction_vec.extend(processed_task.prediction_vec); + } + if accumulated_processed_task.prediction_vec.is_empty() || accumulated_processed_task.ptwotp_vec.is_empty() { + return Err(anyhow::anyhow!("task: {} prediction_vec.is_empty() or ptwotp_vec.is_empty(). It's supposed to be non-empty.", task.id)); + } + Ok(accumulated_processed_task) + } + + fn process_task_item(task: &Task, variant: u8) -> anyhow::Result { + let count_test: u8 = task.count_test().min(255) as u8; + if count_test < 1 { + return Err(anyhow::anyhow!("skipping task: {} because it has no test pairs", task.id)); + } + + let context = ProcessTaskContext::new(task, variant); + + let task_for_processing: Task = task.clone(); + let prediction_type: arcathon_solution_coordinator::PredictionType; + if task.is_output_size_same_as_input_size() { + prediction_type = arcathon_solution_coordinator::PredictionType::SolveLogisticRegressionSameSize; + } else { + prediction_type = arcathon_solution_coordinator::PredictionType::SolveLogisticRegressionDifferentSize; + } + + let mut ptwotp_vec = Vec::::new(); + for test_index in 0..count_test { + let ptwotp: ProcessedTaskWithOneTestPair = match Self::process_task_with_one_test_pair(&context, &task_for_processing, test_index) { + Ok(value) => value, + Err(error) => { + return Err(error); + } + }; + ptwotp_vec.push(ptwotp); + } + + let mut prediction_vec = Vec::::new(); + for (test_index, ptwotp) in ptwotp_vec.iter().enumerate() { + let grid: arc_json_model::Grid = arc_json_model::Grid::from_image(&ptwotp.cropped_image); + let prediction = arcathon_solution_coordinator::Prediction { + output_id: test_index.min(255) as u8, + output: grid, + prediction_type, + }; + prediction_vec.push(prediction); + } + + if prediction_vec.len() != (count_test as usize) { + return Err(anyhow::anyhow!("task: {} predictions.len() != task.count_test()", task.id)); + } + let instance = ProcessedTask { + ptwotp_vec, + prediction_vec, + }; + Ok(instance) + } + + fn process_task_with_one_test_pair(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result { + if context.input_size_vec.len() != task.pairs.len() { + return Err(anyhow::anyhow!("context.output_size_vec.len() != task.pairs.len()")); + } + if context.output_size_vec.len() != task.pairs.len() { + return Err(anyhow::anyhow!("context.output_size_vec.len() != task.pairs.len()")); + } + + // Obtain `pair_index` from `test_index`. + let mut found_pair_index: Option = None; + for pair in &task.pairs { + if pair.test_index == Some(test_index) { + found_pair_index = Some(pair.pair_index); + break; + } + } + let pair_index: u8 = match found_pair_index { + Some(value) => value, + None => { + return Err(anyhow::anyhow!("Unable to find pair with test_index: {}", test_index)); + } + }; + + let mut available_colors = Histogram::new(); + for color in 0..=9u8 { + available_colors.increment(color); + } + + // If all pairs agree on the same removal colors, then make sure none of these are present in the available colors. + available_colors.subtract_histogram(&task.removal_histogram_intersection); + + if task.insert_histogram_intersection.number_of_counters_greater_than_zero() > 0 { + available_colors = task.insert_histogram_intersection.clone(); + } + + // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_descending())); + HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); + // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors)); + + let mut predicted_color: u8 = 42; + + if available_colors.number_of_counters_greater_than_zero() == 1 { + if let Some(color) = available_colors.most_popular_color() { + predicted_color = color; + } + } + + // If there are 3 or fewer colors, then make a prediction for each color. + // If there are 4 or more colors, then do extra work to make max 3 predictions. + // if available_colors.number_of_counters_greater_than_zero() <= 3 { + // }; + + let crop_output_size: ImageSize = context.output_size_vec[pair_index as usize]; + + let cropped_image: Image = Image::color(crop_output_size.width, crop_output_size.height, predicted_color); + + let instance = ProcessedTaskWithOneTestPair { + test_index, + cropped_image, + inspect_internal_image_vec: vec!(), + }; + Ok(instance) + } + + /// This solver is only able to solve tasks where all pairs agrees that the output images have one color. + fn all_pairs_have_one_output_color(task: &Task) -> bool { + for action_label in &task.action_label_set_intersection { + match action_label { + ActionLabel::OutputImageUniqueColorCount { count } => { + if *count == 1 { + return true; + } + }, + _ => {} + } + } + false + } +} + +/// Returned from `process_task_with_one_test_pair` +struct ProcessedTaskWithOneTestPair { + test_index: u8, + cropped_image: Image, + inspect_internal_image_vec: Vec, +} + +struct ProcessedTask { + ptwotp_vec: Vec, + prediction_vec: Vec, +} From 1642004788263119552996f3074ccf22fd88c0fb Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 15:02:29 +0100 Subject: [PATCH 06/32] output_image_colors_comes_from_input_image() added --- .../loda-rust-cli/src/arc/solve_one_color.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 9e64eb8d..d22fec01 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -363,6 +363,14 @@ impl SolveOneColor { available_colors.increment(color); } + if Self::output_image_colors_comes_from_input_image(task) { + for pair in &task.pairs { + if pair.test_index == Some(test_index) { + available_colors = pair.input.image_meta.histogram_all.clone(); + } + } + } + // If all pairs agree on the same removal colors, then make sure none of these are present in the available colors. available_colors.subtract_histogram(&task.removal_histogram_intersection); @@ -370,6 +378,11 @@ impl SolveOneColor { available_colors = task.insert_histogram_intersection.clone(); } + // All pairs agree on the exact same color. + if task.output_histogram_intersection == task.output_histogram_union { + available_colors = task.output_histogram_intersection.clone(); + } + // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_descending())); HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors)); @@ -413,6 +426,19 @@ impl SolveOneColor { } false } + + /// Does the output colors come from the input image. + fn output_image_colors_comes_from_input_image(task: &Task) -> bool { + for action_label in &task.action_label_set_intersection { + match action_label { + ActionLabel::OutputImageColorsComesFromInputImage => { + return true; + }, + _ => {} + } + } + false + } } /// Returned from `process_task_with_one_test_pair` From d38c99831e5203ae94c8cd7970e21ec3fd4b4d6b Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 15:34:08 +0100 Subject: [PATCH 07/32] Renamed action labels related to Pair special colors --- .../loda-rust-cli/src/arc/arc_work_model_impl_pair.rs | 8 ++++---- rust_project/loda-rust-cli/src/arc/label.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/arc_work_model_impl_pair.rs b/rust_project/loda-rust-cli/src/arc/arc_work_model_impl_pair.rs index 6d49bceb..4c8fe31d 100644 --- a/rust_project/loda-rust-cli/src/arc/arc_work_model_impl_pair.rs +++ b/rust_project/loda-rust-cli/src/arc/arc_work_model_impl_pair.rs @@ -820,19 +820,19 @@ impl arc_work_model::Pair { let output_least_popular_color: Option = self.output.image_meta.histogram_all.least_popular_color_disallow_ambiguous(); if input_most_popular_color.is_some() && input_most_popular_color == output_most_popular_color { - let label = ActionLabel::InputMostPopularColorIsOutputMostPopularColor; + let label = ActionLabel::PairInputMostPopularColorIsOutputMostPopularColor; self.action_label_set.insert(label); } if input_most_popular_color.is_some() && input_most_popular_color == output_least_popular_color { - let label = ActionLabel::InputMostPopularColorIsOutputLeastPopularColor; + let label = ActionLabel::PairInputMostPopularColorIsOutputLeastPopularColor; self.action_label_set.insert(label); } if input_least_popular_color.is_some() && input_least_popular_color == output_most_popular_color { - let label = ActionLabel::InputLeastPopularColorIsOutputMostPopularColor; + let label = ActionLabel::PairInputLeastPopularColorIsOutputMostPopularColor; self.action_label_set.insert(label); } if input_least_popular_color.is_some() && input_least_popular_color == output_least_popular_color { - let label = ActionLabel::InputLeastPopularColorIsOutputLeastPopularColor; + let label = ActionLabel::PairInputLeastPopularColorIsOutputLeastPopularColor; self.action_label_set.insert(label); } diff --git a/rust_project/loda-rust-cli/src/arc/label.rs b/rust_project/loda-rust-cli/src/arc/label.rs index b8a583a7..c78270e8 100644 --- a/rust_project/loda-rust-cli/src/arc/label.rs +++ b/rust_project/loda-rust-cli/src/arc/label.rs @@ -389,16 +389,16 @@ pub enum ActionLabel { OutputImageColorsComesFromInputImage, /// What input colors explains the output colors. The most popular stays the most popular. - InputMostPopularColorIsOutputMostPopularColor, + PairInputMostPopularColorIsOutputMostPopularColor, /// What input colors explains the output colors. The least popular stays the least popular. - InputLeastPopularColorIsOutputLeastPopularColor, + PairInputLeastPopularColorIsOutputLeastPopularColor, /// What input colors explains the output colors. The most popular becomes the least popular. - InputMostPopularColorIsOutputLeastPopularColor, + PairInputMostPopularColorIsOutputLeastPopularColor, /// What input colors explains the output colors. The least popular becomes the most popular. - InputLeastPopularColorIsOutputMostPopularColor, + PairInputLeastPopularColorIsOutputMostPopularColor, /// The output size is the same as the input size. /// Each pixel have the same number of identical pixels as in the input. From cb9a3dc771c9dfcc436f84b73884f9bea3e8feb8 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 15:35:32 +0100 Subject: [PATCH 08/32] First time I'm making use of ActionLabel::PairInputMostPopularColorIsOutputMostPopularColor --- .../loda-rust-cli/src/arc/solve_one_color.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index d22fec01..50f5baa1 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -378,6 +378,12 @@ impl SolveOneColor { available_colors = task.insert_histogram_intersection.clone(); } + // The most popular color specific for each pair, is used for the output color. + if let Some(color) = Self::pair_input_most_popular_color_is_output_most_popular_color(task, pair_index) { + available_colors = Histogram::new(); + available_colors.increment(color); + } + // All pairs agree on the exact same color. if task.output_histogram_intersection == task.output_histogram_union { available_colors = task.output_histogram_intersection.clone(); @@ -439,6 +445,33 @@ impl SolveOneColor { } false } + + /// If the output color the same as the most popular color in the input image for pair. + /// then returns the most popular color for that pair. + fn pair_input_most_popular_color_is_output_most_popular_color(task: &Task, pair_index: u8) -> Option { + let mut found = false; + for action_label in &task.action_label_set_intersection { + match action_label { + ActionLabel::PairInputMostPopularColorIsOutputMostPopularColor => { + found = true; + break; + }, + _ => {} + } + } + if !found { + return None; + } + + // Obtain the most popular color for the specified pair. + for pair in &task.pairs { + if pair.pair_index == pair_index { + return pair.input.image_meta.histogram_all.most_popular_color_disallow_ambiguous(); + } + } + + None + } } /// Returned from `process_task_with_one_test_pair` From f19d258d4486556c98d567bfffc4c5a12f9645c4 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 15:44:30 +0100 Subject: [PATCH 09/32] Extract the least popular color --- .../loda-rust-cli/src/arc/solve_one_color.rs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 50f5baa1..e23a5b96 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -379,7 +379,13 @@ impl SolveOneColor { } // The most popular color specific for each pair, is used for the output color. - if let Some(color) = Self::pair_input_most_popular_color_is_output_most_popular_color(task, pair_index) { + if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { + available_colors = Histogram::new(); + available_colors.increment(color); + } + + // The least popular color specific for each pair, is used for the output color. + if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { available_colors = Histogram::new(); available_colors.increment(color); } @@ -448,7 +454,7 @@ impl SolveOneColor { /// If the output color the same as the most popular color in the input image for pair. /// then returns the most popular color for that pair. - fn pair_input_most_popular_color_is_output_most_popular_color(task: &Task, pair_index: u8) -> Option { + fn pair_input_most_popular_color(task: &Task, pair_index: u8) -> Option { let mut found = false; for action_label in &task.action_label_set_intersection { match action_label { @@ -472,6 +478,33 @@ impl SolveOneColor { None } + + /// If the output color the same as the least popular color in the input image for pair. + /// then returns the least popular color for that pair. + fn pair_input_least_popular_color(task: &Task, pair_index: u8) -> Option { + let mut found = false; + for action_label in &task.action_label_set_intersection { + match action_label { + ActionLabel::PairInputLeastPopularColorIsOutputMostPopularColor => { + found = true; + break; + }, + _ => {} + } + } + if !found { + return None; + } + + // Obtain the most popular color for the specified pair. + for pair in &task.pairs { + if pair.pair_index == pair_index { + return pair.input.image_meta.histogram_all.least_popular_color_disallow_ambiguous(); + } + } + + None + } } /// Returned from `process_task_with_one_test_pair` From e2ba6fa94a2e441a36b9d33a471d52edb83b07f1 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 21:47:43 +0100 Subject: [PATCH 10/32] Removed dead code --- rust_project/loda-rust-cli/src/arc/solve_one_color.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index e23a5b96..5b1c1efd 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -236,7 +236,6 @@ impl SolveOneColor { // Display the internal computed image to the html log if run_and_verify_htmllog { for (index, ptwotp) in processed_task.ptwotp_vec.iter().enumerate() { - HtmlLog::compare_images(ptwotp.inspect_internal_image_vec.clone()); let is_correct: bool = correct_vec[index]; if is_correct { HtmlLog::text(format!("{} - test_index: {} - correct", task.id, ptwotp.test_index)); @@ -402,7 +401,7 @@ impl SolveOneColor { let mut predicted_color: u8 = 42; if available_colors.number_of_counters_greater_than_zero() == 1 { - if let Some(color) = available_colors.most_popular_color() { + if let Some(color) = available_colors.most_popular_color_disallow_ambiguous() { predicted_color = color; } } @@ -419,7 +418,6 @@ impl SolveOneColor { let instance = ProcessedTaskWithOneTestPair { test_index, cropped_image, - inspect_internal_image_vec: vec!(), }; Ok(instance) } @@ -511,7 +509,6 @@ impl SolveOneColor { struct ProcessedTaskWithOneTestPair { test_index: u8, cropped_image: Image, - inspect_internal_image_vec: Vec, } struct ProcessedTask { From 8d2c40c5e67f60ed84f16d9151ac45e9d27d7608 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 22:17:31 +0100 Subject: [PATCH 11/32] Extracted a predict_colors_for_task_with_test_index() that can return multiple colors --- .../loda-rust-cli/src/arc/solve_one_color.rs | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 5b1c1efd..0f18e0ea 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -335,6 +335,48 @@ impl SolveOneColor { } fn process_task_with_one_test_pair(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result { + // Obtain `pair_index` from `test_index`. + let mut found_pair_index: Option = None; + for pair in &task.pairs { + if pair.test_index == Some(test_index) { + found_pair_index = Some(pair.pair_index); + break; + } + } + let pair_index: u8 = match found_pair_index { + Some(value) => value, + None => { + return Err(anyhow::anyhow!("Unable to find pair with test_index: {}", test_index)); + } + }; + + let predicted_colors: Vec = Self::predict_colors_for_task_with_test_index(context, task, test_index)?; + + let mut predicted_color: u8 = 42; + + if predicted_colors.len() == 1 { + if let Some(color) = predicted_colors.first() { + predicted_color = *color; + } + } + + // If there are 3 or fewer colors, then make a prediction for each color. + // If there are 4 or more colors, then do extra work to make max 3 predictions. + // if available_colors.number_of_counters_greater_than_zero() <= 3 { + // }; + + let crop_output_size: ImageSize = context.output_size_vec[pair_index as usize]; + + let cropped_image: Image = Image::color(crop_output_size.width, crop_output_size.height, predicted_color); + + let instance = ProcessedTaskWithOneTestPair { + test_index, + cropped_image, + }; + Ok(instance) + } + + fn predict_colors_for_task_with_test_index(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result> { if context.input_size_vec.len() != task.pairs.len() { return Err(anyhow::anyhow!("context.output_size_vec.len() != task.pairs.len()")); } @@ -398,28 +440,20 @@ impl SolveOneColor { HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors)); - let mut predicted_color: u8 = 42; - if available_colors.number_of_counters_greater_than_zero() == 1 { if let Some(color) = available_colors.most_popular_color_disallow_ambiguous() { - predicted_color = color; + return Ok(vec![color]); } } + + let mut predicted_color: u8 = 42; // If there are 3 or fewer colors, then make a prediction for each color. // If there are 4 or more colors, then do extra work to make max 3 predictions. // if available_colors.number_of_counters_greater_than_zero() <= 3 { // }; - let crop_output_size: ImageSize = context.output_size_vec[pair_index as usize]; - - let cropped_image: Image = Image::color(crop_output_size.width, crop_output_size.height, predicted_color); - - let instance = ProcessedTaskWithOneTestPair { - test_index, - cropped_image, - }; - Ok(instance) + Ok(vec![predicted_color]) } /// This solver is only able to solve tasks where all pairs agrees that the output images have one color. @@ -494,7 +528,7 @@ impl SolveOneColor { return None; } - // Obtain the most popular color for the specified pair. + // Obtain the least popular color for the specified pair. for pair in &task.pairs { if pair.pair_index == pair_index { return pair.input.image_meta.histogram_all.least_popular_color_disallow_ambiguous(); From 85f6c3d4a6ea97262e0329d2374f3289892457ab Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 23:15:40 +0100 Subject: [PATCH 12/32] Eliminated dangerous assumption that enumeration index is the same as test_index. --- rust_project/loda-rust-cli/src/arc/solve_one_color.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 0f18e0ea..59b4d0b1 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -314,10 +314,10 @@ impl SolveOneColor { } let mut prediction_vec = Vec::::new(); - for (test_index, ptwotp) in ptwotp_vec.iter().enumerate() { + for ptwotp in &ptwotp_vec { let grid: arc_json_model::Grid = arc_json_model::Grid::from_image(&ptwotp.cropped_image); let prediction = arcathon_solution_coordinator::Prediction { - output_id: test_index.min(255) as u8, + output_id: ptwotp.test_index.min(255) as u8, output: grid, prediction_type, }; From 4f8484e127c010ef58d157294f9b65bad9aa5514 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 23:17:05 +0100 Subject: [PATCH 13/32] Eliminated dangerous assumption that enumeration index is the same as test_index. --- .../loda-rust-cli/src/arc/solve_logisticregression.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index d37cc5fa..64e82626 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -675,10 +675,10 @@ impl SolveLogisticRegression { } let mut prediction_vec = Vec::::new(); - for (test_index, ptwotp) in ptwotp_vec.iter().enumerate() { + for ptwotp in &ptwotp_vec { let grid: arc_json_model::Grid = arc_json_model::Grid::from_image(&ptwotp.cropped_image); let prediction = arcathon_solution_coordinator::Prediction { - output_id: test_index.min(255) as u8, + output_id: ptwotp.test_index.min(255) as u8, output: grid, prediction_type, }; From c587be12dc29392faacfbe5d1fd763770188c9a3 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 23:37:57 +0100 Subject: [PATCH 14/32] process_task_with_one_test_pair() now returns a vector of predictions. --- .../loda-rust-cli/src/arc/solve_one_color.rs | 95 ++++++------------- 1 file changed, 29 insertions(+), 66 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 59b4d0b1..7d76e0fe 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -18,8 +18,6 @@ use std::sync::{Arc, Mutex}; use rayon::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; -const PROCESS_TASK_VARIANTS: [u8; 1] = [0]; - #[derive(Clone, Debug)] enum ProcessTaskMode { InputOutputSameSize, @@ -28,7 +26,6 @@ enum ProcessTaskMode { #[derive(Clone, Debug)] pub struct ProcessTaskContext { - variant: u8, mode: ProcessTaskMode, input_size_vec: Vec, output_size_vec: Vec, @@ -36,14 +33,13 @@ pub struct ProcessTaskContext { } impl ProcessTaskContext { - pub fn new(task: &Task, variant: u8) -> Self { + pub fn new(task: &Task) -> Self { let mode: ProcessTaskMode = if task.is_output_size_same_as_input_size() { ProcessTaskMode::InputOutputSameSize } else { ProcessTaskMode::InputOutputDifferentSize }; let mut instance = Self { - variant, mode, input_size_vec: Vec::::new(), output_size_vec: Vec::::new(), @@ -268,31 +264,12 @@ impl SolveOneColor { } fn process_task(task: &Task) -> anyhow::Result { - let mut accumulated_processed_task = ProcessedTask { - ptwotp_vec: vec!(), - prediction_vec: vec!(), - }; - - for variant in &PROCESS_TASK_VARIANTS { - let processed_task: ProcessedTask = Self::process_task_item(task, *variant) - .with_context(|| format!("task: {} Unable to process_task_item() with variant: {}", task.id, variant))?; - - accumulated_processed_task.ptwotp_vec.extend(processed_task.ptwotp_vec); - accumulated_processed_task.prediction_vec.extend(processed_task.prediction_vec); - } - if accumulated_processed_task.prediction_vec.is_empty() || accumulated_processed_task.ptwotp_vec.is_empty() { - return Err(anyhow::anyhow!("task: {} prediction_vec.is_empty() or ptwotp_vec.is_empty(). It's supposed to be non-empty.", task.id)); - } - Ok(accumulated_processed_task) - } - - fn process_task_item(task: &Task, variant: u8) -> anyhow::Result { let count_test: u8 = task.count_test().min(255) as u8; if count_test < 1 { return Err(anyhow::anyhow!("skipping task: {} because it has no test pairs", task.id)); } - let context = ProcessTaskContext::new(task, variant); + let context = ProcessTaskContext::new(task); let task_for_processing: Task = task.clone(); let prediction_type: arcathon_solution_coordinator::PredictionType; @@ -302,19 +279,19 @@ impl SolveOneColor { prediction_type = arcathon_solution_coordinator::PredictionType::SolveLogisticRegressionDifferentSize; } - let mut ptwotp_vec = Vec::::new(); + let mut accumulated_ptwotp_vec = Vec::::new(); for test_index in 0..count_test { - let ptwotp: ProcessedTaskWithOneTestPair = match Self::process_task_with_one_test_pair(&context, &task_for_processing, test_index) { + let ptwotp_vec: Vec = match Self::process_task_with_one_test_pair(&context, &task_for_processing, test_index) { Ok(value) => value, Err(error) => { return Err(error); } }; - ptwotp_vec.push(ptwotp); + accumulated_ptwotp_vec.extend(ptwotp_vec); } let mut prediction_vec = Vec::::new(); - for ptwotp in &ptwotp_vec { + for ptwotp in &accumulated_ptwotp_vec { let grid: arc_json_model::Grid = arc_json_model::Grid::from_image(&ptwotp.cropped_image); let prediction = arcathon_solution_coordinator::Prediction { output_id: ptwotp.test_index.min(255) as u8, @@ -324,17 +301,14 @@ impl SolveOneColor { prediction_vec.push(prediction); } - if prediction_vec.len() != (count_test as usize) { - return Err(anyhow::anyhow!("task: {} predictions.len() != task.count_test()", task.id)); - } let instance = ProcessedTask { - ptwotp_vec, + ptwotp_vec: accumulated_ptwotp_vec, prediction_vec, }; Ok(instance) } - fn process_task_with_one_test_pair(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result { + fn process_task_with_one_test_pair(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result> { // Obtain `pair_index` from `test_index`. let mut found_pair_index: Option = None; for pair in &task.pairs { @@ -351,29 +325,18 @@ impl SolveOneColor { }; let predicted_colors: Vec = Self::predict_colors_for_task_with_test_index(context, task, test_index)?; - - let mut predicted_color: u8 = 42; - - if predicted_colors.len() == 1 { - if let Some(color) = predicted_colors.first() { - predicted_color = *color; - } - } - - // If there are 3 or fewer colors, then make a prediction for each color. - // If there are 4 or more colors, then do extra work to make max 3 predictions. - // if available_colors.number_of_counters_greater_than_zero() <= 3 { - // }; - let crop_output_size: ImageSize = context.output_size_vec[pair_index as usize]; + let output_size: ImageSize = context.output_size_vec[pair_index as usize]; - let cropped_image: Image = Image::color(crop_output_size.width, crop_output_size.height, predicted_color); - - let instance = ProcessedTaskWithOneTestPair { - test_index, - cropped_image, - }; - Ok(instance) + let ptwotp_vec: Vec = predicted_colors.iter().map(|predicted_color| { + let cropped_image: Image = Image::color(output_size.width, output_size.height, *predicted_color); + let ptwotp = ProcessedTaskWithOneTestPair { + test_index, + cropped_image, + }; + ptwotp + }).collect(); + Ok(ptwotp_vec) } fn predict_colors_for_task_with_test_index(context: &ProcessTaskContext, task: &Task, test_index: u8) -> anyhow::Result> { @@ -436,22 +399,22 @@ impl SolveOneColor { available_colors = task.output_histogram_intersection.clone(); } - // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_descending())); HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); - // HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors)); - if available_colors.number_of_counters_greater_than_zero() == 1 { - if let Some(color) = available_colors.most_popular_color_disallow_ambiguous() { - return Ok(vec![color]); - } + if available_colors.number_of_counters_greater_than_zero() == 0 { + bail!("Unable to make prediction for task: {} - test_index: {} there are no available colors", task.id, test_index); } - let mut predicted_color: u8 = 42; + // If there are 3 or fewer colors, then make a prediction with each color. ARCathon allows for 3 predictions. + if available_colors.number_of_counters_greater_than_zero() <= 3 { + let colors: Vec = available_colors.pairs_ordered_by_color().iter().map(|pair| pair.1).collect(); + return Ok(colors); + } + + // There are 4 or more colors, then do extra work to make max 3 predictions. - // If there are 3 or fewer colors, then make a prediction for each color. - // If there are 4 or more colors, then do extra work to make max 3 predictions. - // if available_colors.number_of_counters_greater_than_zero() <= 3 { - // }; + // return Err(anyhow::anyhow!("Unable to make prediction for task: {} - test_index: {} there are too many colors", task.id, test_index)); + let mut predicted_color: u8 = 42; Ok(vec![predicted_color]) } From 60cbe220fef42c96e29b2602a5d42c0b316ae69c Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Fri, 24 Nov 2023 23:55:18 +0100 Subject: [PATCH 15/32] debugging --- rust_project/loda-rust-cli/src/arc/solve_one_color.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 7d76e0fe..ab632a7c 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -223,6 +223,8 @@ impl SolveOneColor { count_solved_partial.fetch_add(1, Ordering::Relaxed); pb.println(format!("task {} - solved partial, {} correct of {} test pairs", task.id, number_of_solved_test_pairs, task_count_test)); HtmlLog::text(format!("task {} - solved partial, {} correct of {} test pairs", task.id, number_of_solved_test_pairs, task_count_test)); + } else { + pb.println(format!("task {} - unable to solve! zero of {} test pairs", task.id, task_count_test)); } } let count_full: usize = count_solved_full.load(Ordering::Relaxed); From 00cb9cf661921b92e859186243a6b8179e6f72d3 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 00:42:47 +0100 Subject: [PATCH 16/32] If all pairs agree on the same color, then return that color. --- .../loda-rust-cli/src/arc/solve_one_color.rs | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index ab632a7c..041579a5 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -364,42 +364,62 @@ impl SolveOneColor { } }; + // All pairs agree on the exact same color. + if task.output_histogram_intersection == task.output_histogram_union { + let available_colors: Histogram = task.output_histogram_intersection.clone(); + // println!("step0: task: {} - test_index: {} - available_colors: {:?} only one color is used across all outputs.", task.id, test_index, available_colors.pairs_ordered_by_color()); + let colors: Vec = available_colors.pairs_ordered_by_color().iter().map(|pair| pair.1).collect(); + return Ok(colors); + } + let mut available_colors = Histogram::new(); for color in 0..=9u8 { available_colors.increment(color); } + // println!("step0: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + if Self::output_image_colors_comes_from_input_image(task) { for pair in &task.pairs { if pair.test_index == Some(test_index) { available_colors = pair.input.image_meta.histogram_all.clone(); + // println!("step1A: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); } } + } else { + available_colors = task.output_histogram_union.clone(); + // println!("step1B: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); } // If all pairs agree on the same removal colors, then make sure none of these are present in the available colors. available_colors.subtract_histogram(&task.removal_histogram_intersection); + // println!("step3: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); if task.insert_histogram_intersection.number_of_counters_greater_than_zero() > 0 { available_colors = task.insert_histogram_intersection.clone(); + // println!("step4: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); } - // The most popular color specific for each pair, is used for the output color. - if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { - available_colors = Histogram::new(); - available_colors.increment(color); - } - - // The least popular color specific for each pair, is used for the output color. - if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { - available_colors = Histogram::new(); - available_colors.increment(color); - } + // if task.output_histogram_union.number_of_counters_greater_than_zero() > 0 { + // available_colors = task.output_histogram_union.clone(); + // println!("step7: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + // } - // All pairs agree on the exact same color. - if task.output_histogram_intersection == task.output_histogram_union { - available_colors = task.output_histogram_intersection.clone(); - } + // The most popular color specific for each pair, is used for the output color. + // if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { + // available_colors = Histogram::new(); + // available_colors.increment(color); + // println!("step5: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + // } + + // // The least popular color specific for each pair, is used for the output color. + // if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { + // available_colors = Histogram::new(); + // available_colors.increment(color); + // println!("step6: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + // } + + // println!("step-last: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); From 0de0b6808df41a8864d9c1cfc722ef79119aff07 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 01:01:48 +0100 Subject: [PATCH 17/32] Debugging --- .../loda-rust-cli/src/arc/solve_one_color.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 041579a5..2833e572 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -364,8 +364,10 @@ impl SolveOneColor { } }; - // All pairs agree on the exact same color. - if task.output_histogram_intersection == task.output_histogram_union { + let output_image_colors_comes_from_input_image: bool = Self::output_image_colors_comes_from_input_image(task); + + // All pairs agree on the exact same color, and the color isn't impacted by the input image. + if output_image_colors_comes_from_input_image == false && task.output_histogram_intersection == task.output_histogram_union { let available_colors: Histogram = task.output_histogram_intersection.clone(); // println!("step0: task: {} - test_index: {} - available_colors: {:?} only one color is used across all outputs.", task.id, test_index, available_colors.pairs_ordered_by_color()); let colors: Vec = available_colors.pairs_ordered_by_color().iter().map(|pair| pair.1).collect(); @@ -379,7 +381,8 @@ impl SolveOneColor { // println!("step0: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - if Self::output_image_colors_comes_from_input_image(task) { + if output_image_colors_comes_from_input_image { + // The output colors are dictated by the input image for pair in &task.pairs { if pair.test_index == Some(test_index) { available_colors = pair.input.image_meta.histogram_all.clone(); @@ -387,22 +390,25 @@ impl SolveOneColor { } } } else { + // The output colors are not dictated by the input image available_colors = task.output_histogram_union.clone(); // println!("step1B: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); } + available_colors.add_histogram(&task.insert_histogram_intersection); + // println!("step2: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + // If all pairs agree on the same removal colors, then make sure none of these are present in the available colors. + // Future improvement: + // ARCathon allows for 3 predictions, we want to make all 3 predictions with different colors. + // By removing too many colors, and we can make fewer predictions. + // Aim for 3 colors in the available colors, after removing the removal colors. available_colors.subtract_histogram(&task.removal_histogram_intersection); // println!("step3: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - if task.insert_histogram_intersection.number_of_counters_greater_than_zero() > 0 { - available_colors = task.insert_histogram_intersection.clone(); - // println!("step4: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - } - // if task.output_histogram_union.number_of_counters_greater_than_zero() > 0 { // available_colors = task.output_histogram_union.clone(); - // println!("step7: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + // println!("step4: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); // } // The most popular color specific for each pair, is used for the output color. @@ -434,6 +440,7 @@ impl SolveOneColor { } // There are 4 or more colors, then do extra work to make max 3 predictions. + println!("do more work: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); // return Err(anyhow::anyhow!("Unable to make prediction for task: {} - test_index: {} there are too many colors", task.id, test_index)); let mut predicted_color: u8 = 42; From 343fa28b51a1bbf84635b0c729f6edbe7a4fe414 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 01:23:22 +0100 Subject: [PATCH 18/32] PredictionType::SolveOneColor added --- .../src/arc/arcathon_solution_coordinator.rs | 10 ++++++++-- .../loda-rust-cli/src/arc/solve_one_color.rs | 12 ++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/arcathon_solution_coordinator.rs b/rust_project/loda-rust-cli/src/arc/arcathon_solution_coordinator.rs index 3ff7f5a7..92c10a9e 100644 --- a/rust_project/loda-rust-cli/src/arc/arcathon_solution_coordinator.rs +++ b/rust_project/loda-rust-cli/src/arc/arcathon_solution_coordinator.rs @@ -27,6 +27,9 @@ pub enum PredictionType { SolveSplit, SolveGenetic, + + /// Number of unique colors the output images across all train pairs, is s a single color. + SolveOneColor, } impl PredictionType { @@ -34,11 +37,14 @@ impl PredictionType { fn sort_weight(&self) -> u32 { match self { // Split tries out lots of things deterministic, so it's high priority. - Self::SolveSplit => 0, + Self::SolveSplit => 0, + + // The SolveOneColor is a very simple solver, so it's high priority. + Self::SolveOneColor => 1, // The LODA programs that have been manually been coded are somewhat good and deals with many edge cases. // The mutated LODA programs, may not deal with edge cases, but they are still good, since all train+test pairs gets evaluated. - Self::SolveGenetic => 1, + Self::SolveGenetic => 2, // Logistic regression is rarely correct, so it's low priority. // It's best at tasks where `input_size == output_size`. diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 2833e572..f1fa3ee1 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -273,17 +273,9 @@ impl SolveOneColor { let context = ProcessTaskContext::new(task); - let task_for_processing: Task = task.clone(); - let prediction_type: arcathon_solution_coordinator::PredictionType; - if task.is_output_size_same_as_input_size() { - prediction_type = arcathon_solution_coordinator::PredictionType::SolveLogisticRegressionSameSize; - } else { - prediction_type = arcathon_solution_coordinator::PredictionType::SolveLogisticRegressionDifferentSize; - } - let mut accumulated_ptwotp_vec = Vec::::new(); for test_index in 0..count_test { - let ptwotp_vec: Vec = match Self::process_task_with_one_test_pair(&context, &task_for_processing, test_index) { + let ptwotp_vec: Vec = match Self::process_task_with_one_test_pair(&context, task, test_index) { Ok(value) => value, Err(error) => { return Err(error); @@ -298,7 +290,7 @@ impl SolveOneColor { let prediction = arcathon_solution_coordinator::Prediction { output_id: ptwotp.test_index.min(255) as u8, output: grid, - prediction_type, + prediction_type: arcathon_solution_coordinator::PredictionType::SolveOneColor, }; prediction_vec.push(prediction); } From 13d4577fa661ca942c5533202835bb031997e1f9 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 12:43:38 +0100 Subject: [PATCH 19/32] SolveOneColor.run_predictions() added --- .../src/arc/solve_logisticregression.rs | 2 +- .../loda-rust-cli/src/arc/solve_one_color.rs | 75 ++++++++++++++++--- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index 64e82626..3fc829cd 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -558,7 +558,7 @@ impl SolveLogisticRegression { /// This code is intended to run with the hidden ARC dataset, which doesn't contain expected output for the test pairs. pub fn run_predictions(&self) -> anyhow::Result { let number_of_tasks: u64 = self.tasks.len() as u64; - println!("{} - run start - will process {} tasks with logistic regression", human_readable_utc_timestamp(), number_of_tasks); + println!("{} - run start - will process {} tasks with SolveLogisticRegression", human_readable_utc_timestamp(), number_of_tasks); let count_solved = AtomicUsize::new(0); let pb = ProgressBar::new(number_of_tasks as u64); pb.set_style(ProgressStyle::default_bar() diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index f1fa3ee1..5a2f7ffb 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -152,15 +152,6 @@ impl SolveOneColor { self.tasks.par_iter().for_each(|task| { pb.inc(1); - // Only process tasks where all pairs agree that the output images have just one color. - if !Self::all_pairs_have_one_output_color(task) { - return; - } - HtmlLog::text(format!("task {}", task.id)); - - // Only process tasks where the task has a predicted size. - // if the task doesn't have a predicted size, then skip it. - let task_count_test: usize = task.count_test(); // Make predictions @@ -265,12 +256,78 @@ impl SolveOneColor { Ok(()) } + /// Run without verifying that the predictions are correct. + /// + /// This code is intended to run with the hidden ARC dataset, which doesn't contain expected output for the test pairs. + pub fn run_predictions(&self) -> anyhow::Result { + let number_of_tasks: u64 = self.tasks.len() as u64; + println!("{} - run start - will process {} tasks with SolveOneColor", human_readable_utc_timestamp(), number_of_tasks); + let count_solved = AtomicUsize::new(0); + let pb = ProgressBar::new(number_of_tasks as u64); + pb.set_style(ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) {msg}")? + .progress_chars("#>-") + ); + let accumulated = Arc::new(Mutex::new(TaskNameToPredictionVec::new())); + self.tasks.par_iter().for_each(|task| { + pb.inc(1); + + // Make predictions + let processed_task: ProcessedTask = match Self::process_task(task) { + Ok(value) => value, + Err(error) => { + pb.println(format!("task {} - error: {:?}", task.id, error)); + return; + } + }; + + // Show progress + count_solved.fetch_add(1, Ordering::Relaxed); + let count: usize = count_solved.load(Ordering::Relaxed); + pb.set_message(format!("Solved: {}", count)); + pb.println(format!("task {} - solved", task.id)); + + // Accumulate the predictions + match accumulated.lock() { + Ok(mut map) => { + map.entry(task.id.clone()) + .or_insert(Vec::new()) + .extend(processed_task.prediction_vec); + }, + Err(error) => { + pb.println(format!("run_predictions. Unable to lock accumulated. error: {:?}", error)); + } + }; + }); + pb.finish_and_clear(); + let count_solved: usize = count_solved.load(Ordering::Relaxed); + println!("{} - run - end", human_readable_utc_timestamp()); + println!("{} - solved {} of {} tasks", human_readable_utc_timestamp(), count_solved, number_of_tasks); + let taskname_to_prediction_vec: TaskNameToPredictionVec = match accumulated.lock() { + Ok(map) => map.clone(), + Err(error) => { + return Err(anyhow::anyhow!("run_predictions. taskname_to_prediction_vec. Unable to lock accumulated. error: {:?}", error)); + } + }; + Ok(taskname_to_prediction_vec) + } + fn process_task(task: &Task) -> anyhow::Result { let count_test: u8 = task.count_test().min(255) as u8; if count_test < 1 { return Err(anyhow::anyhow!("skipping task: {} because it has no test pairs", task.id)); } + // Only process tasks where all pairs agree that the output images have just one color. + if !Self::all_pairs_have_one_output_color(task) { + return Err(anyhow::anyhow!("skipping task: {} all_pairs_have_one_output_color is not satisfied", task.id)); + } + HtmlLog::text(format!("task {}", task.id)); + + // Future experiments: + // Only process tasks where the task has a predicted size with high confidence. + // if the task doesn't have a predicted size, then skip it, so that another solver can try solve it. + let context = ProcessTaskContext::new(task); let mut accumulated_ptwotp_vec = Vec::::new(); From 63b12b51353c75e331cdfdec83a1924a5eef5893 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 12:56:42 +0100 Subject: [PATCH 20/32] Integrating the SolveOneColor into the arc-competition subcommand --- .../src/arc/traverse_programs_and_models.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs index 195d6951..fe8a6999 100644 --- a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs +++ b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs @@ -1,5 +1,5 @@ use super::arc_work_model::{PairType, Task}; -use super::{RunWithProgram, RunWithProgramResult}; +use super::{RunWithProgram, RunWithProgramResult, SolveOneColor}; use super::ArcathonSolutionJsonFile; use super::{ActionLabel, ImageHistogram, ImageSize, Histogram, ExportTasks, SolveSplit}; use super::{SolveSplitFoundSolution, ArcathonSolutionCoordinator}; @@ -1591,6 +1591,10 @@ impl TraverseProgramsAndModels { // and don't want to spend time on solving already solved tasks. let resume_from_last_snapshot = false; + // Solve tasks that outputs a single color. + // Not yet tried on the hidden ARC dataset, so I don't know if this doesn't solves any tasks. + let try_solve_one_color = false; + // Solve `splitview` like tasks. // On the hidden ARC dataset, this doesn't solve any tasks. let try_solve_split = true; @@ -1617,6 +1621,7 @@ impl TraverseProgramsAndModels { println!("path_solution_teamid_json: {:?}", self.arc_config.path_solution_teamid_json); println!("resume_from_last_snapshot: {}", resume_from_last_snapshot); + println!("try_solve_one_color: {}", try_solve_one_color); println!("try_solve_split: {}", try_solve_split); println!("try_existing_solutions: {}", try_existing_solutions); println!("try_logistic_regression: {}", try_logistic_regression); @@ -1708,6 +1713,24 @@ impl TraverseProgramsAndModels { println!("Number of tasks unsolved: {}", count_unsolved); } + if try_solve_one_color { + let solver_start_time: Instant = Instant::now(); + println!("{} - SolveOneColor - start", human_readable_utc_timestamp()); + let task_vec: Vec = self.to_task_vec(); + let instance = SolveOneColor::new(task_vec); + match instance.run_predictions() { + Ok(taskname_to_predictions) => { + println!("SolveOneColor::run_predictions completed successfully"); + coordinator.append_predictions_from_hashmap(&taskname_to_predictions); + }, + Err(error) => { + error!("SolveOneColor::run_with_callback failed with error: {:?}", error); + } + } + coordinator.save_solutions_json_with_console_output(); + println!("{} - SolveOneColor - end. Elapsed {}", human_readable_utc_timestamp(), HumanDuration(solver_start_time.elapsed())); + } + if try_solve_split { let solver_start_time: Instant = Instant::now(); let number_of_tasks: u64 = self.model_item_vec.len() as u64; From 95167f21644567b411d09e66673cbf775968b308 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 12:59:33 +0100 Subject: [PATCH 21/32] Renamed from try_logistic_regression to try_solve_logistic_regression --- .../loda-rust-cli/src/arc/traverse_programs_and_models.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs index fe8a6999..2f33ac11 100644 --- a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs +++ b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs @@ -1600,8 +1600,8 @@ impl TraverseProgramsAndModels { let try_solve_split = true; // Run logistic regression. - // On the hidden ARC dataset, this solves 1 task. - let try_logistic_regression = true; + // On the hidden ARC dataset, this solves 2 tasks. + let try_solve_logistic_regression = true; // Try out the existing programs with the unsolved tasks. // This may be a solution to one of the hidden puzzles. @@ -1623,8 +1623,8 @@ impl TraverseProgramsAndModels { println!("resume_from_last_snapshot: {}", resume_from_last_snapshot); println!("try_solve_one_color: {}", try_solve_one_color); println!("try_solve_split: {}", try_solve_split); + println!("try_solve_logistic_regression: {}", try_solve_logistic_regression); println!("try_existing_solutions: {}", try_existing_solutions); - println!("try_logistic_regression: {}", try_logistic_regression); println!("try_mutate_existing_solutions: {}", try_mutate_existing_solutions); println!("ARC_COMPETITION_NUMBER_OF_MUTATIONS: {}", ARC_COMPETITION_NUMBER_OF_MUTATIONS); @@ -1775,7 +1775,7 @@ impl TraverseProgramsAndModels { println!("{} - SolveSplit - complete - solved {} of {} tasks. Elapsed {}", human_readable_utc_timestamp(), count_tasks_solved, number_of_tasks, HumanDuration(solver_start_time.elapsed())); } - if try_logistic_regression { + if try_solve_logistic_regression { let solver_start_time: Instant = Instant::now(); println!("{} - SolveLogisticRegression - start", human_readable_utc_timestamp()); let task_vec: Vec = self.to_task_vec(); From 2f1c1de76c0ebdebfbc95c8e5bd98e4037e67a1e Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 13:33:20 +0100 Subject: [PATCH 22/32] SolveOneColor now only considers the tasks that have one output color. so now errors gets printed. --- .../loda-rust-cli/src/arc/solve_one_color.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 5a2f7ffb..6eb69d42 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -127,8 +127,11 @@ pub struct SolveOneColor { impl SolveOneColor { pub fn new(tasks: Vec) -> Self { // println!("loaded {} tasks", tasks.len()); + let count0: usize = tasks.len(); + let tasks_for_processing: Vec = tasks.iter().filter(|task| Self::can_process_task(task)).cloned().collect(); + println!("SolveOneColor::new() out of {} tasks, {} tasks will be processed", count0, tasks_for_processing.len()); Self { - tasks, + tasks: tasks_for_processing, } } @@ -312,6 +315,14 @@ impl SolveOneColor { Ok(taskname_to_prediction_vec) } + fn can_process_task(task: &Task) -> bool { + // Only process tasks where all pairs agree that the output images have just one color. + if !Self::all_pairs_have_one_output_color(task) { + return false; + } + true + } + fn process_task(task: &Task) -> anyhow::Result { let count_test: u8 = task.count_test().min(255) as u8; if count_test < 1 { From 03dd69a605a2aa2b140d921f0f4ac6eb2d6e17e4 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 13:56:03 +0100 Subject: [PATCH 23/32] eliminated warnings --- rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index 3fc829cd..38ff8f3b 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -389,6 +389,7 @@ impl ProcessTaskContext { struct ProcessedTaskWithOneTestPair { test_index: u8, cropped_image: Image, + #[allow(dead_code)] inspect_internal_image_vec: Vec, } @@ -414,6 +415,7 @@ impl SolveLogisticRegression { /// This can be run with the public ARC dataset contains expected output for the test pairs. /// /// This cannot be run with the hidden ARC dataset, which doesn't contain expected output for the test pairs. + #[allow(dead_code)] pub fn run_and_verify(&self) -> anyhow::Result<()> { let run_and_verify_htmllog = true; let run_and_verify_ignore_already_solved = false; From 0644defec0805f04c2adf5e67cb75746b1eccaf4 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 13:56:35 +0100 Subject: [PATCH 24/32] Removed dead code --- .../loda-rust-cli/src/arc/solve_one_color.rs | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 6eb69d42..67f41562 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -1,32 +1,24 @@ //! Solve tasks that outputs a single color. //! -//! Example: -//! d631b094 +//! The ARC 1 dataset contains 800 tasks, where 17 of the tasks outputs a single color. +//! 1190e5a7, 1a2e2828, 239be575, 23b5c85d, 27a28665, 3194b014, 445eab21, 44f52bb0, 5582e5ca, 642d658d, +//! 7039b2d7, 8597cfd7, b9b7f026, d631b094, d9fac9be, de1cd16c, e872b94a, +//! use super::arc_json_model::GridFromImage; use super::arc_work_model::{Task, PairType, Pair}; -use super::{Image, ImageOverlay, arcathon_solution_coordinator, arc_json_model, ImageMix, MixMode, ObjectsAndMass, ImageCrop, Rectangle, ImageExtractRowColumn, ImageDenoise, TaskGraph, ShapeType, ImageSize, ShapeTransformation, SingleColorObject, ShapeIdentificationFromSingleColorObject, ImageDetectHole, ImagePadding, ImageRepairPattern, TaskNameToPredictionVec, CreateTaskWithSameSize, ImageReplaceColor, ImageCenterIndicator, ImageGravity, GravityDirection, DiagonalHistogram, RecordTrigram, ImageNgram, ImageExteriorCorners, LargestInteriorRectangle, ImageDrawRect, PropertyOutput, ImageProperty, ImageResize, ImageRepeat, rule, CellularAutomaton, ChangeItem}; -use super::{ActionLabel, ImageLabel, ImageMaskDistance, LineSpan, LineSpanDirection, LineSpanMode, VerifyPrediction, VerifyPredictionWithTask}; -use super::{HtmlLog, PixelConnectivity, ImageHistogram, Histogram, ImageEdge, ImageMask}; -use super::{ImageNeighbour, ImageNeighbourDirection, ImageCornerAnalyze, ImageMaskGrow, Shape3x3}; +use super::{Image, arcathon_solution_coordinator, arc_json_model, ImageSize, TaskNameToPredictionVec, PropertyOutput, ImageProperty}; +use super::{ActionLabel, VerifyPrediction, VerifyPredictionWithTask}; +use super::{HtmlLog, Histogram}; use super::human_readable_utc_timestamp; -use anyhow::{Context, bail}; +use anyhow::bail; use indicatif::{ProgressBar, ProgressStyle}; -use serde::Serialize; -use std::borrow::BorrowMut; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::sync::{Arc, Mutex}; use rayon::prelude::*; use std::sync::atomic::{AtomicUsize, Ordering}; -#[derive(Clone, Debug)] -enum ProcessTaskMode { - InputOutputSameSize, - InputOutputDifferentSize, -} - #[derive(Clone, Debug)] pub struct ProcessTaskContext { - mode: ProcessTaskMode, input_size_vec: Vec, output_size_vec: Vec, scale_widthheight: Option<(u8, u8)>, @@ -34,13 +26,7 @@ pub struct ProcessTaskContext { impl ProcessTaskContext { pub fn new(task: &Task) -> Self { - let mode: ProcessTaskMode = if task.is_output_size_same_as_input_size() { - ProcessTaskMode::InputOutputSameSize - } else { - ProcessTaskMode::InputOutputDifferentSize - }; let mut instance = Self { - mode, input_size_vec: Vec::::new(), output_size_vec: Vec::::new(), scale_widthheight: None, @@ -140,6 +126,7 @@ impl SolveOneColor { /// This can be run with the public ARC dataset contains expected output for the test pairs. /// /// This cannot be run with the hidden ARC dataset, which doesn't contain expected output for the test pairs. + #[allow(dead_code)] pub fn run_and_verify(&self) -> anyhow::Result<()> { let run_and_verify_htmllog = true; let number_of_tasks: u64 = self.tasks.len() as u64; @@ -417,7 +404,7 @@ impl SolveOneColor { break; } } - let pair_index: u8 = match found_pair_index { + let _pair_index: u8 = match found_pair_index { Some(value) => value, None => { return Err(anyhow::anyhow!("Unable to find pair with test_index: {}", test_index)); @@ -503,7 +490,7 @@ impl SolveOneColor { println!("do more work: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); // return Err(anyhow::anyhow!("Unable to make prediction for task: {} - test_index: {} there are too many colors", task.id, test_index)); - let mut predicted_color: u8 = 42; + let predicted_color: u8 = 42; Ok(vec![predicted_color]) } @@ -538,6 +525,7 @@ impl SolveOneColor { /// If the output color the same as the most popular color in the input image for pair. /// then returns the most popular color for that pair. + #[allow(dead_code)] fn pair_input_most_popular_color(task: &Task, pair_index: u8) -> Option { let mut found = false; for action_label in &task.action_label_set_intersection { @@ -565,6 +553,7 @@ impl SolveOneColor { /// If the output color the same as the least popular color in the input image for pair. /// then returns the least popular color for that pair. + #[allow(dead_code)] fn pair_input_least_popular_color(task: &Task, pair_index: u8) -> Option { let mut found = false; for action_label in &task.action_label_set_intersection { From af60faa53c5a6c9be658e62225bdee0d972652ec Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 15:02:59 +0100 Subject: [PATCH 25/32] Introduced an "arc-solve" subcommand. This replaces the long subcommand "arc-logistic-regression" with "arc-solve lr". Invoking the SolveOneColor looks like this "arc-solve one". --- .../src/arc/traverse_programs_and_models.rs | 17 ++++++++++++--- rust_project/loda-rust-cli/src/main.rs | 21 ++++++++++++------- .../loda-rust-cli/src/subcommand_arc.rs | 18 +++++++++------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs index 2f33ac11..5eb045fa 100644 --- a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs +++ b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs @@ -12,7 +12,7 @@ use crate::common::{find_json_files_recursively, parse_csv_file, create_csv_file use crate::common::find_asm_files_recursively; use crate::mine::{Genome, GenomeItem, ToGenomeItemVec, CreateGenomeMutateContextMode, create_genome_mutate_context, GenomeMutateContext}; use bloomfilter::*; -use anyhow::Context; +use anyhow::{Context, bail}; use loda_rust_core::control::DependencyManager; use loda_rust_core::execute::{ProgramSerializer, ProgramId, ProgramRunner}; use loda_rust_core::parser::ParsedProgram; @@ -88,18 +88,29 @@ impl TraverseProgramsAndModels { Ok(()) } - pub fn solve_with_logistic_regression() -> anyhow::Result<()> { + pub fn solve_with_specific_solver(name_of_solver: &String) -> anyhow::Result<()> { // let tpam = TraverseProgramsAndModels::new()?; // let task_vec: Vec = tpam.to_task_vec(); // let mut instance = ExperimentWithConvolution::new(task_vec); // instance.run()?; - { + + if name_of_solver == "lr" { let tpam = TraverseProgramsAndModels::new()?; let task_vec: Vec = tpam.to_task_vec(); let instance = SolveLogisticRegression::new(task_vec); instance.run_and_verify()?; return Ok(()); } + + if name_of_solver == "one" { + let tpam = TraverseProgramsAndModels::new()?; + let task_vec: Vec = tpam.to_task_vec(); + let instance = SolveOneColor::new(task_vec); + instance.run_and_verify()?; + return Ok(()); + } + + bail!("Unknown solver: {}", name_of_solver); } pub fn export_dataset() -> anyhow::Result<()> { diff --git a/rust_project/loda-rust-cli/src/main.rs b/rust_project/loda-rust-cli/src/main.rs index 9bf68449..34c21013 100644 --- a/rust_project/loda-rust-cli/src/main.rs +++ b/rust_project/loda-rust-cli/src/main.rs @@ -161,7 +161,7 @@ async fn main() -> anyhow::Result<()> { ) .subcommand( Command::new("arc-generate-solution-csv") - .about("ARC - Populate the 'solutions.csv' file by trying out all puzzles with all solutions.") + .about("ARC - Populate the 'solutions.csv' file by trying out all tasks with all solutions.") .hide(true) ) .subcommand( @@ -171,7 +171,7 @@ async fn main() -> anyhow::Result<()> { ) .subcommand( Command::new("arc-label") - .about("ARC - Traverse all puzzles and classify each puzzle.") + .about("ARC - Traverse all tasks and classify each puzzle.") .hide(true) ) .subcommand( @@ -180,9 +180,14 @@ async fn main() -> anyhow::Result<()> { .hide(true) ) .subcommand( - Command::new("arc-logistic-regression") - .about("ARC - Solve all puzzles using logistic regression solver.") + Command::new("arc-solve") + .about("ARC - Run a specific solver with all the tasks and check if the prediction are correct.") .hide(true) + .arg( + Arg::new("nameofsolver") + .help("Name of the solver. lr = SolveLogisticRegression, one = SolveOneColor.") + .required(true) + ) ) .subcommand( Command::new("arc-size") @@ -190,7 +195,7 @@ async fn main() -> anyhow::Result<()> { .hide(true) .arg( Arg::new("file") - .help("Path to the task json file. Example: /home/arc-dataset/evaluation/0123abcd.json") + .help("Absolute path to the task json file. Example: /home/arc-dataset/evaluation/0123abcd.json") .required(true) ) ) @@ -350,9 +355,11 @@ async fn main() -> anyhow::Result<()> { return Ok(()); } - if let Some(_sub_m) = matches.subcommand_matches("arc-logistic-regression") { + if let Some(sub_m) = matches.subcommand_matches("arc-solve") { + let nameofsolver_raw: &str = sub_m.value_of("nameofsolver").expect("nameofsolver"); + let mode = SubcommandARCMode::SolveWithSpecificSolver { name_of_solver: nameofsolver_raw.to_string() }; let blocking_task = tokio::task::spawn_blocking(|| { - SubcommandARC::run(SubcommandARCMode::SolveWithLogisticRegression).expect("ok"); + SubcommandARC::run(mode).expect("ok"); }); blocking_task.await?; return Ok(()); diff --git a/rust_project/loda-rust-cli/src/subcommand_arc.rs b/rust_project/loda-rust-cli/src/subcommand_arc.rs index 546926c1..c61ad2bd 100644 --- a/rust_project/loda-rust-cli/src/subcommand_arc.rs +++ b/rust_project/loda-rust-cli/src/subcommand_arc.rs @@ -25,9 +25,13 @@ pub enum SubcommandARCMode { /// Create a file with training data. ExportDataset, - /// Run all tasks using the logistic regression solver. - SolveWithLogisticRegression, - + /// Run all tasks using the specified solver. + /// + /// where `name_of_solver` is one of: + /// - `lr` is logistic regression. + /// - `one` is `SolveOneColor`. + SolveWithSpecificSolver { name_of_solver: String }, + /// Predict the output sizes for a single ARC task. PredictOutputSizesForSingleTask { task_json_file: PathBuf }, } @@ -59,13 +63,13 @@ impl SubcommandARC { return TraverseProgramsAndModels::label_all_puzzles(); }, SubcommandARCMode::ExportDataset => { - return TraverseProgramsAndModels::export_dataset(); - }, - SubcommandARCMode::SolveWithLogisticRegression => { // let path: PathBuf = PathBuf::from("/Users/neoneye/Downloads/histograms.jsonl"); // GenerateDataset::generate_fulldataset(&path)?; // return Ok(()); - return TraverseProgramsAndModels::solve_with_logistic_regression(); + return TraverseProgramsAndModels::export_dataset(); + }, + SubcommandARCMode::SolveWithSpecificSolver { name_of_solver } => { + return TraverseProgramsAndModels::solve_with_specific_solver(&name_of_solver); }, SubcommandARCMode::PredictOutputSizesForSingleTask { task_json_file } => { return SubcommandARCSize::run(&task_json_file); From 25660347437a4d4707a592015674ed7a4fd31fea Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 16:58:16 +0100 Subject: [PATCH 26/32] Histogram.color_vec() added. --- .../loda-rust-cli/src/arc/histogram.rs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/histogram.rs b/rust_project/loda-rust-cli/src/arc/histogram.rs index ef3a6f03..cb94d778 100644 --- a/rust_project/loda-rust-cli/src/arc/histogram.rs +++ b/rust_project/loda-rust-cli/src/arc/histogram.rs @@ -327,7 +327,7 @@ impl Histogram { self.least_popular().count_allow_ambiguous() } - /// The pairs ordered by their color value. + /// The pairs ordered by their color value. Ignoring their count. /// /// The lowest color value comes first. /// @@ -342,6 +342,16 @@ impl Histogram { pairs } + /// The colors ordered by their color value. Ignoring their count. + /// + /// The lowest color value comes first. + /// + /// The highest color value comes last. + #[allow(dead_code)] + pub fn color_vec(&self) -> Vec { + self.pairs_ordered_by_color().iter().map(|pair| pair.1).collect() + } + /// The least frequent occurring comes first. /// /// The medium frequent occurring comes middle. @@ -850,7 +860,24 @@ mod tests { } #[test] - fn test_50001_pairs_descending() { + fn test_50001_color_vec() { + // Arrange + let mut h = Histogram::new(); + let values: [u8; 8] = [3, 42, 42, 3, 2, 3, 4, 5]; + for value in values { + h.increment(value); + } + + // Act + let actual: Vec = h.color_vec(); + + // Assert + let expected: Vec = vec![2, 3, 4, 5, 42]; + assert_eq!(actual, expected); + } + + #[test] + fn test_50002_pairs_descending() { // Arrange let mut h = Histogram::new(); let values: [u8; 12] = [3, 1, 1, 42, 7, 42, 7, 3, 2, 3, 4, 5]; @@ -867,7 +894,7 @@ mod tests { } #[test] - fn test_50002_pairs_ascending() { + fn test_50003_pairs_ascending() { // Arrange let mut h = Histogram::new(); let values: [u8; 12] = [3, 1, 1, 42, 7, 42, 7, 3, 2, 3, 4, 5]; From 7c0841eb7407243c3a58f5c04de4df76baf905f6 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 18:02:39 +0100 Subject: [PATCH 27/32] SolveOneColor now solves 16 tasks out of the 800 tasks. --- .../loda-rust-cli/src/arc/solve_one_color.rs | 89 ++++++++++++------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 67f41562..74efe0d4 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -4,6 +4,24 @@ //! 1190e5a7, 1a2e2828, 239be575, 23b5c85d, 27a28665, 3194b014, 445eab21, 44f52bb0, 5582e5ca, 642d658d, //! 7039b2d7, 8597cfd7, b9b7f026, d631b094, d9fac9be, de1cd16c, e872b94a, //! +//! This solver is able to solve 16 of the 17 tasks. +//! Where 10 is solved with some confidence. +//! Where 6 of them is solved as a happy accident. +//! Where 1 task is not solved, because it has more than 4 colors to choose from, and chooses the wrong color. +//! +//! Weakness: +//! When there are 4 or more colors to choose from, then it doesn't do any prediction, and takes the 3 first colors. +//! If the 3 first colors are not the correct colors, then it will fail. +//! The real solution will be to do the actual work needed to bring down the number of colors to max 3 colors, +//! by doing shape recognition, eliminating noise colors. +//! These tasks are exceeding 3 colors: 1a2e2828, 27a28665, 3194b014, 642d658d, b9b7f026, de1cd16c. +//! and thus are not solved by this solver. They are a happy accident if they are "guessed" correctly. +//! +//! Future experiments: +//! Compare shapes across the input, is there a correlation between the shape and the output color. +//! Check symmetry in the input, is there a correlation between the symmetry and the output color. +//! Identify the densest color clusters, and pick the most popular color from the densest cluster. +//! Count number of holes, and return the object with the most holes or least holes. use super::arc_json_model::GridFromImage; use super::arc_work_model::{Task, PairType, Pair}; use super::{Image, arcathon_solution_coordinator, arc_json_model, ImageSize, TaskNameToPredictionVec, PropertyOutput, ImageProperty}; @@ -404,7 +422,7 @@ impl SolveOneColor { break; } } - let _pair_index: u8 = match found_pair_index { + let pair_index: u8 = match found_pair_index { Some(value) => value, None => { return Err(anyhow::anyhow!("Unable to find pair with test_index: {}", test_index)); @@ -417,7 +435,7 @@ impl SolveOneColor { if output_image_colors_comes_from_input_image == false && task.output_histogram_intersection == task.output_histogram_union { let available_colors: Histogram = task.output_histogram_intersection.clone(); // println!("step0: task: {} - test_index: {} - available_colors: {:?} only one color is used across all outputs.", task.id, test_index, available_colors.pairs_ordered_by_color()); - let colors: Vec = available_colors.pairs_ordered_by_color().iter().map(|pair| pair.1).collect(); + let colors: Vec = available_colors.color_vec(); return Ok(colors); } @@ -450,49 +468,56 @@ impl SolveOneColor { // ARCathon allows for 3 predictions, we want to make all 3 predictions with different colors. // By removing too many colors, and we can make fewer predictions. // Aim for 3 colors in the available colors, after removing the removal colors. + // Move the colors to a 3rd bin for the weakest predictions. available_colors.subtract_histogram(&task.removal_histogram_intersection); // println!("step3: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - // if task.output_histogram_union.number_of_counters_greater_than_zero() > 0 { - // available_colors = task.output_histogram_union.clone(); - // println!("step4: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - // } + let mut primary_color_predictions = Histogram::new(); // The most popular color specific for each pair, is used for the output color. - // if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { - // available_colors = Histogram::new(); - // available_colors.increment(color); - // println!("step5: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - // } - - // // The least popular color specific for each pair, is used for the output color. - // if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { - // available_colors = Histogram::new(); - // available_colors.increment(color); - // println!("step6: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - // } + if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { + primary_color_predictions.increment(color); + available_colors.set_counter_to_zero(color); + println!("step5: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); + } + + // The least popular color specific for each pair, is used for the output color. + if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { + primary_color_predictions.increment(color); + available_colors.set_counter_to_zero(color); + println!("step6: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); + } // println!("step-last: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - HtmlLog::text(format!("task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color())); + HtmlLog::text(format!("task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color())); - if available_colors.number_of_counters_greater_than_zero() == 0 { + let primary_count: u16 = primary_color_predictions.number_of_counters_greater_than_zero(); + let secondary_count: u16 = available_colors.number_of_counters_greater_than_zero(); + let total_count: u16 = primary_count + secondary_count; + if total_count == 0 { bail!("Unable to make prediction for task: {} - test_index: {} there are no available colors", task.id, test_index); } - - // If there are 3 or fewer colors, then make a prediction with each color. ARCathon allows for 3 predictions. - if available_colors.number_of_counters_greater_than_zero() <= 3 { - let colors: Vec = available_colors.pairs_ordered_by_color().iter().map(|pair| pair.1).collect(); - return Ok(colors); - } - // There are 4 or more colors, then do extra work to make max 3 predictions. - println!("do more work: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); + let mut the_colors: Vec = primary_color_predictions.color_vec(); + the_colors.extend(available_colors.color_vec()); - // return Err(anyhow::anyhow!("Unable to make prediction for task: {} - test_index: {} there are too many colors", task.id, test_index)); - let predicted_color: u8 = 42; - - Ok(vec![predicted_color]) + let high_confidence: bool = primary_count > 0 && primary_count <= 3; + if high_confidence { + println!("high confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + } else { + let medium_confidence: bool = secondary_count > 0 && secondary_count <= 3; + if medium_confidence { + println!("medium confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + } else { + // There are 4 or more colors to choose from. Extra work is needed to bring down the number of colors to make max 3 predictions. + // Future experiments: + // Assign a low confidence score to these predictions, so they are ranked lower than other high confidence predictions. + // Rule out the noise color, grid color, most dense colors. + println!("low confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + } + } + Ok(the_colors) } /// This solver is only able to solve tasks where all pairs agrees that the output images have one color. From d5df158c32db7ed85380615c57e59a9a2ed89bb8 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 19:04:02 +0100 Subject: [PATCH 28/32] Disabled debugging --- .../loda-rust-cli/src/arc/solve_one_color.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs index 74efe0d4..4e3e8f5a 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_one_color.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_one_color.rs @@ -7,7 +7,7 @@ //! This solver is able to solve 16 of the 17 tasks. //! Where 10 is solved with some confidence. //! Where 6 of them is solved as a happy accident. -//! Where 1 task is not solved, because it has more than 4 colors to choose from, and chooses the wrong color. +//! Where 1 task is not solved b9b7f026, because it has more than 4 colors to choose from, and chooses the wrong color. //! //! Weakness: //! When there are 4 or more colors to choose from, then it doesn't do any prediction, and takes the 3 first colors. @@ -338,7 +338,6 @@ impl SolveOneColor { if !Self::all_pairs_have_one_output_color(task) { return Err(anyhow::anyhow!("skipping task: {} all_pairs_have_one_output_color is not satisfied", task.id)); } - HtmlLog::text(format!("task {}", task.id)); // Future experiments: // Only process tasks where the task has a predicted size with high confidence. @@ -478,19 +477,18 @@ impl SolveOneColor { if let Some(color) = Self::pair_input_most_popular_color(task, pair_index) { primary_color_predictions.increment(color); available_colors.set_counter_to_zero(color); - println!("step5: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); + // println!("step5: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); } // The least popular color specific for each pair, is used for the output color. if let Some(color) = Self::pair_input_least_popular_color(task, pair_index) { primary_color_predictions.increment(color); available_colors.set_counter_to_zero(color); - println!("step6: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); + // println!("step6: task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color()); } // println!("step-last: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, available_colors.pairs_ordered_by_color()); - - HtmlLog::text(format!("task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color())); + // HtmlLog::text(format!("task: {} - test_index: {} - primary_predictions: {:?} available_colors: {:?}", task.id, test_index, primary_color_predictions.pairs_ordered_by_color(), available_colors.pairs_ordered_by_color())); let primary_count: u16 = primary_color_predictions.number_of_counters_greater_than_zero(); let secondary_count: u16 = available_colors.number_of_counters_greater_than_zero(); @@ -504,17 +502,17 @@ impl SolveOneColor { let high_confidence: bool = primary_count > 0 && primary_count <= 3; if high_confidence { - println!("high confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + debug!("high confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); } else { let medium_confidence: bool = secondary_count > 0 && secondary_count <= 3; if medium_confidence { - println!("medium confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + debug!("medium confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); } else { // There are 4 or more colors to choose from. Extra work is needed to bring down the number of colors to make max 3 predictions. // Future experiments: // Assign a low confidence score to these predictions, so they are ranked lower than other high confidence predictions. // Rule out the noise color, grid color, most dense colors. - println!("low confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); + debug!("low confidence: task: {} - test_index: {} - available_colors: {:?}", task.id, test_index, the_colors); } } Ok(the_colors) @@ -550,7 +548,6 @@ impl SolveOneColor { /// If the output color the same as the most popular color in the input image for pair. /// then returns the most popular color for that pair. - #[allow(dead_code)] fn pair_input_most_popular_color(task: &Task, pair_index: u8) -> Option { let mut found = false; for action_label in &task.action_label_set_intersection { @@ -578,7 +575,6 @@ impl SolveOneColor { /// If the output color the same as the least popular color in the input image for pair. /// then returns the least popular color for that pair. - #[allow(dead_code)] fn pair_input_least_popular_color(task: &Task, pair_index: u8) -> Option { let mut found = false; for action_label in &task.action_label_set_intersection { From 85853b297bfac3ee0cb13c0142a97ee738b29c19 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 19:23:02 +0100 Subject: [PATCH 29/32] Enabled SolveOneColor when running in ARCathon. --- .../loda-rust-cli/src/arc/traverse_programs_and_models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs index 5eb045fa..18162dcd 100644 --- a/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs +++ b/rust_project/loda-rust-cli/src/arc/traverse_programs_and_models.rs @@ -1604,7 +1604,7 @@ impl TraverseProgramsAndModels { // Solve tasks that outputs a single color. // Not yet tried on the hidden ARC dataset, so I don't know if this doesn't solves any tasks. - let try_solve_one_color = false; + let try_solve_one_color = true; // Solve `splitview` like tasks. // On the hidden ARC dataset, this doesn't solve any tasks. From d08069b56c92c54140c27d13f8e6ad0897824d5a Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sat, 25 Nov 2023 19:51:40 +0100 Subject: [PATCH 30/32] Bumped version, and submitted new ARCathon 2023 docker image. --- rust_project/Cargo.lock | 2 +- .../payload/.loda-rust/analytics-arc/analytics_log.txt | 2 +- .../.loda-rust/analytics-arc/last_analytics_timestamp.txt | 2 +- rust_project/loda-rust-cli/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust_project/Cargo.lock b/rust_project/Cargo.lock index 42018362..76cc9c71 100644 --- a/rust_project/Cargo.lock +++ b/rust_project/Cargo.lock @@ -2124,7 +2124,7 @@ dependencies = [ [[package]] name = "loda-rust-cli" -version = "2023.11.23" +version = "2023.11.25" dependencies = [ "ahash", "alphanumeric-sort", diff --git a/rust_project/arc-competition/payload/.loda-rust/analytics-arc/analytics_log.txt b/rust_project/arc-competition/payload/.loda-rust/analytics-arc/analytics_log.txt index 02ed8f98..cccdee61 100644 --- a/rust_project/arc-competition/payload/.loda-rust/analytics-arc/analytics_log.txt +++ b/rust_project/arc-competition/payload/.loda-rust/analytics-arc/analytics_log.txt @@ -32,4 +32,4 @@ optimize: 0, dontoptimize: 1, optimize/total: 0.0% AnalyzeProgramModified timestamps: 88 -subcommand_analytics finished, elapsed: 20 ms +subcommand_analytics finished, elapsed: 21 ms diff --git a/rust_project/arc-competition/payload/.loda-rust/analytics-arc/last_analytics_timestamp.txt b/rust_project/arc-competition/payload/.loda-rust/analytics-arc/last_analytics_timestamp.txt index fdcf8759..d93ef37b 100644 --- a/rust_project/arc-competition/payload/.loda-rust/analytics-arc/last_analytics_timestamp.txt +++ b/rust_project/arc-competition/payload/.loda-rust/analytics-arc/last_analytics_timestamp.txt @@ -1 +1 @@ -2023-11-23T01:19:08Z \ No newline at end of file +2023-11-25T18:24:46Z \ No newline at end of file diff --git a/rust_project/loda-rust-cli/Cargo.toml b/rust_project/loda-rust-cli/Cargo.toml index 07f70843..a3355f0e 100644 --- a/rust_project/loda-rust-cli/Cargo.toml +++ b/rust_project/loda-rust-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "loda-rust-cli" -version = "2023.11.23" +version = "2023.11.25" authors = ["Simon Strandgaard "] description = "Command line interface for LODA Rust" repository = "https://github.com/loda-lang/loda-rust" From e210909f4db0436a5c8896d7979f5c0e00d435b6 Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sun, 26 Nov 2023 22:30:04 +0100 Subject: [PATCH 31/32] updated status --- .../loda-rust-cli/src/arc/solve_logisticregression.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs index 38ff8f3b..1e1f186a 100644 --- a/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs +++ b/rust_project/loda-rust-cli/src/arc/solve_logisticregression.rs @@ -1,6 +1,10 @@ //! Performs logistic regression of each input pixel with the corresponding classification for the output pixel. //! //! These older commits solves some of the tasks from the hidden ARC dataset, using logistic regression: +//! commit 2023-Nov-25: solves 2 of the hidden ARC tasks. This uses variant=0 and variant=1 and variant=2. Only variant=0 does something useful. +//! Since it uses multiple variants and doesn't solve more tasks than the previous commit, then it is not an improvement. +//! https://github.com/loda-lang/loda-rust/commit/d08069b56c92c54140c27d13f8e6ad0897824d5a +//! //! commit 2023-Nov-23: solves 2 of the hidden ARC tasks. This uses variant=0 and variant=1 and variant=2. Only variant=0 does something useful. //! Since it uses multiple variants and doesn't solve more tasks than the previous commit, then it is not an improvement. //! https://github.com/loda-lang/loda-rust/commit/2e6577cd66af0c00fdd65533d4770a40ed2ccf3c From 5dd32439f74f225316fd49c4a4a67f389d18b34a Mon Sep 17 00:00:00 2001 From: Simon Strandgaard Date: Sun, 26 Nov 2023 22:31:08 +0100 Subject: [PATCH 32/32] disabled loda-rust-arc --- rust_project/loda-rust-cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust_project/loda-rust-cli/Cargo.toml b/rust_project/loda-rust-cli/Cargo.toml index a3355f0e..4e5696b4 100644 --- a/rust_project/loda-rust-cli/Cargo.toml +++ b/rust_project/loda-rust-cli/Cargo.toml @@ -12,7 +12,7 @@ name = "loda-rust" path = "src/main.rs" [features] -default = ["loda-rust-arc"] +# default = ["loda-rust-arc"] loda-rust-arc = ["dep:petgraph", "dep:image_crate", "dep:linfa", "dep:linfa-logistic", "dep:linfa-preprocessing", "dep:ndarray", "dep:rayon"] [dependencies]