Compare commits

...

3 Commits

Author SHA1 Message Date
Emilia Allison 3d8445ec82
Formatting on tests
Gitea Scan/plate-tool/pipeline/head This commit looks good Details
2024-11-15 15:28:34 -06:00
Emilia Allison 04554f634b
fix: Optimize region transfer map 2024-11-15 15:28:01 -06:00
Emilia Allison ad3bbd3649
fix: Typo in transfer validation 2024-11-15 15:27:18 -06:00
1 changed files with 116 additions and 84 deletions

View File

@ -131,10 +131,8 @@ impl TransferRegion {
pub fn calculate_map(&self) -> Box<dyn Fn(Well) -> Option<Vec<Well>> + '_> { pub fn calculate_map(&self) -> Box<dyn Fn(Well) -> Option<Vec<Well>> + '_> {
// By validating first, we have a stronger guarantee that // By validating first, we have a stronger guarantee that
// this function will not panic. :) // this function will not panic. :)
// log::debug!("Validating: {:?}", self.validate());
if let Err(msg) = self.validate() { if let Err(msg) = self.validate() {
eprintln!("{}", msg); log::error!("{}\nThis transfer will be empty.", msg);
eprintln!("This transfer will be empty.");
return Box::new(|_| None); return Box::new(|_| None);
} }
@ -204,22 +202,30 @@ impl TransferRegion {
); );
let count = ( let count = (
// How many times can we replicate? // How many times can we replicate?
if il_dest.0.unsigned_abs() == 0 {
1
} else {
(1..) (1..)
.position(|n| { .position(|n| {
n * number_used_src_wells.0 * il_dest.0.unsigned_abs() (n * number_used_src_wells.0 * il_dest.0.unsigned_abs())
- il_dest.0.unsigned_abs() .saturating_sub(il_dest.0.unsigned_abs())
+ 1 + 1
> d_dims.0 > d_dims.0
}) })
.unwrap() as u8, .unwrap() as u8
},
if il_dest.1.unsigned_abs() == 0 {
1
} else {
(1..) (1..)
.position(|n| { .position(|n| {
n * number_used_src_wells.1 * il_dest.1.unsigned_abs() (n * number_used_src_wells.1 * il_dest.1.unsigned_abs())
- il_dest.1.unsigned_abs() .saturating_sub(il_dest.1.unsigned_abs())
+ 1 + 1
> d_dims.1 > d_dims.1
}) })
.unwrap() as u8, .unwrap() as u8
},
); );
let i = i let i = i
.saturating_sub(s_ul.row) .saturating_sub(s_ul.row)
@ -227,30 +233,34 @@ impl TransferRegion {
let j = j let j = j
.saturating_sub(s_ul.col) .saturating_sub(s_ul.col)
.saturating_div(il_source.1.unsigned_abs()); .saturating_div(il_source.1.unsigned_abs());
let checked_il_dest = (u8::max(il_dest.0.unsigned_abs(), 1u8),
u8::max(il_dest.1.unsigned_abs(), 1u8));
let row_modulus = number_used_src_wells.0 * checked_il_dest.0;
let column_modulus = number_used_src_wells.1 * checked_il_dest.1;
Some( Some(
possible_destination_wells possible_destination_wells
.into_iter() .into_iter()
.filter(|Well { row: x , ..}| { .filter(|Well { row: x, .. }| {
x.checked_sub(d_ul.row).unwrap() x.checked_sub(d_ul.row).unwrap()
% (number_used_src_wells.0 * il_dest.0.unsigned_abs()) // Counter along x % row_modulus // Counter along x
== (il_dest.0.unsigned_abs() *i) == (il_dest.0.unsigned_abs() *i)
% (number_used_src_wells.0 * il_dest.0.unsigned_abs()) % row_modulus
}) })
.filter(|Well { col: y, .. }| { .filter(|Well { col: y, .. }| {
y.checked_sub(d_ul.col).unwrap() y.checked_sub(d_ul.col).unwrap()
% (number_used_src_wells.1 * il_dest.1.unsigned_abs()) // Counter along u % column_modulus // Counter along u
== (il_dest.1.unsigned_abs() *j) == (il_dest.1.unsigned_abs() *j)
% (number_used_src_wells.1 * il_dest.1.unsigned_abs()) % column_modulus
}) })
.filter(|Well { row: x, col: y }| { .filter(|Well { row: x, col: y }| {
// How many times have we replicated? < How many are we allowed // How many times have we replicated? < How many are we allowed
// to replicate? // to replicate?
x.checked_sub(d_ul.row).unwrap().div_euclid( x.checked_sub(d_ul.row).unwrap().div_euclid(
number_used_src_wells.0 * il_dest.0.unsigned_abs(), row_modulus
) < count.0 ) < count.0
&& y.checked_sub(d_ul.col).unwrap().div_euclid( && y.checked_sub(d_ul.col).unwrap().div_euclid(
number_used_src_wells.1 * il_dest.1.unsigned_abs(), column_modulus
) < count.1 ) < count.1
}) })
.collect(), .collect(),
@ -308,11 +318,15 @@ impl TransferRegion {
// log::debug!("s1.1: {}, max.1: {}", s1.1, source_max.1); // log::debug!("s1.1: {}, max.1: {}", s1.1, source_max.1);
return Err("Source region is out-of-bounds! (Too wide)"); return Err("Source region is out-of-bounds! (Too wide)");
} }
if il_dest == (0,0) {
return Err("Refusing to pool both dimensions in a rectangular transfer!\nPlease select a point in the destination plate.")
}
} }
Region::Custom(_) => return Ok(()), Region::Custom(_) => return Ok(()),
} }
if il_source.0 == 0 || il_dest.1 == 0 { if il_source.0 == 0 || il_source.1 == 0 {
return Err("Source interleave cannot be zero!"); return Err("Source interleave cannot be zero!");
} }
@ -409,85 +423,85 @@ mod tests {
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: source, source_plate: source,
source_region: Region::Rect(Well{ row: 1, col: 1}, Well { row: 1, col: 1 }), source_region: Region::Rect(Well { row: 1, col: 1 }, Well { row: 1, col: 1 }),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point(Well {row: 3, col: 3}), dest_region: Region::Point(Well { row: 3, col: 3 }),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (1, 1), interleave_dest: (1, 1),
}; };
let transfer1_map = transfer1.calculate_map(); let transfer1_map = transfer1.calculate_map();
assert_eq!( assert_eq!(
transfer1_map(Well {row: 1, col: 1}), transfer1_map(Well { row: 1, col: 1 }),
Some(vec! {Well {row: 3, col: 3}}), Some(vec! {Well {row: 3, col: 3}}),
"Failed basic shift transfer 1" "Failed basic shift transfer 1"
); );
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 1, col: 2}), transfer1_map(Well { row: 1, col: 2 }),
Some(vec! {Well{ row: 3, col: 4}}), Some(vec! {Well{ row: 3, col: 4}}),
"Failed basic shift transfer 2" "Failed basic shift transfer 2"
); );
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 2, col: 2}), transfer1_map(Well { row: 2, col: 2 }),
Some(vec! {Well{ row: 4, col: 4}}), Some(vec! {Well{ row: 4, col: 4}}),
"Failed basic shift transfer 3" "Failed basic shift transfer 3"
); );
let transfer2 = TransferRegion { let transfer2 = TransferRegion {
source_plate: source, source_plate: source,
source_region: Region::Rect(Well{ row: 1, col: 1}, Well{ row: 3, col: 3}), source_region: Region::Rect(Well { row: 1, col: 1 }, Well { row: 3, col: 3 }),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point(Well{ row: 3, col: 3}), dest_region: Region::Point(Well { row: 3, col: 3 }),
interleave_source: (2, 2), interleave_source: (2, 2),
interleave_dest: (1, 1), interleave_dest: (1, 1),
}; };
let transfer2_map = transfer2.calculate_map(); let transfer2_map = transfer2.calculate_map();
assert_eq!( assert_eq!(
transfer2_map(Well{ row: 1, col: 1}), transfer2_map(Well { row: 1, col: 1 }),
Some(vec! {Well{ row: 3, col: 3}}), Some(vec! {Well{ row: 3, col: 3}}),
"Failed source interleave, type simple 1" "Failed source interleave, type simple 1"
); );
assert_eq!( assert_eq!(
transfer2_map(Well{ row: 1, col: 2}), transfer2_map(Well { row: 1, col: 2 }),
None, None,
"Failed source interleave, type simple 2" "Failed source interleave, type simple 2"
); );
assert_eq!( assert_eq!(
transfer2_map(Well{ row: 2, col: 2}), transfer2_map(Well { row: 2, col: 2 }),
None, None,
"Failed source interleave, type simple 3" "Failed source interleave, type simple 3"
); );
assert_eq!( assert_eq!(
transfer2_map(Well{ row: 3, col: 3}), transfer2_map(Well { row: 3, col: 3 }),
Some(vec! {Well{ row: 4, col: 4}}), Some(vec! {Well{ row: 4, col: 4}}),
"Failed source interleave, type simple 4" "Failed source interleave, type simple 4"
); );
let transfer3 = TransferRegion { let transfer3 = TransferRegion {
source_plate: source, source_plate: source,
source_region: Region::Rect(Well{ row: 1, col: 1}, Well{ row: 3, col: 3}), source_region: Region::Rect(Well { row: 1, col: 1 }, Well { row: 3, col: 3 }),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point(Well{ row: 3, col: 3}), dest_region: Region::Point(Well { row: 3, col: 3 }),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (2, 3), interleave_dest: (2, 3),
}; };
let transfer3_map = transfer3.calculate_map(); let transfer3_map = transfer3.calculate_map();
assert_eq!( assert_eq!(
transfer3_map(Well{ row: 1, col: 1}), transfer3_map(Well { row: 1, col: 1 }),
Some(vec! {Well{ row: 3, col: 3}}), Some(vec! {Well{ row: 3, col: 3}}),
"Failed destination interleave, type simple 1" "Failed destination interleave, type simple 1"
); );
assert_eq!( assert_eq!(
transfer3_map(Well{ row: 2, col: 1}), transfer3_map(Well { row: 2, col: 1 }),
Some(vec! {Well{ row: 5, col: 3}}), Some(vec! {Well{ row: 5, col: 3}}),
"Failed destination interleave, type simple 2" "Failed destination interleave, type simple 2"
); );
assert_eq!( assert_eq!(
transfer3_map(Well{ row: 1, col: 2}), transfer3_map(Well { row: 1, col: 2 }),
Some(vec! {Well{ row: 3, col: 6}}), Some(vec! {Well{ row: 3, col: 6}}),
"Failed destination interleave, type simple 3" "Failed destination interleave, type simple 3"
); );
assert_eq!( assert_eq!(
transfer3_map(Well{ row: 2, col: 2}), transfer3_map(Well { row: 2, col: 2 }),
Some(vec! {Well{ row: 5, col: 6}}), Some(vec! {Well{ row: 5, col: 6}}),
"Failed destination interleave, type simple 4" "Failed destination interleave, type simple 4"
); );
@ -500,29 +514,33 @@ mod tests {
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: source, source_plate: source,
source_region: Region::Rect(Well{ row: 1, col: 1}, Well{ row: 2, col: 2}), source_region: Region::Rect(Well { row: 1, col: 1 }, Well { row: 2, col: 2 }),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Rect(Well{ row: 2, col: 2}, Well{row:11, col:11}), dest_region: Region::Rect(Well { row: 2, col: 2 }, Well { row: 11, col: 11 }),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (3, 3), interleave_dest: (3, 3),
}; };
let transfer1_map = transfer1.calculate_map(); let transfer1_map = transfer1.calculate_map();
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 1, col: 1}), transfer1_map(Well { row: 1, col: 1 }),
Some(vec! {Well{ row: 2, col: 2}, Well{ row: 2, col: 8}, Well{ row: 8, col: 2}, Well{ row: 8, col: 8}}), Some(
vec! {Well{ row: 2, col: 2}, Well{ row: 2, col: 8}, Well{ row: 8, col: 2}, Well{ row: 8, col: 8}}
),
"Failed type replicate 1" "Failed type replicate 1"
); );
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 2, col: 1}), transfer1_map(Well { row: 2, col: 1 }),
Some(vec! {Well{ row: 5, col: 2}, Well{ row: 5, col: 8}, Well{ row: 11, col: 2}, Well{ row: 11, col: 8}}), Some(
vec! {Well{ row: 5, col: 2}, Well{ row: 5, col: 8}, Well{ row: 11, col: 2}, Well{ row: 11, col: 8}}
),
"Failed type replicate 1" "Failed type replicate 1"
); );
let transfer2 = TransferRegion { let transfer2 = TransferRegion {
source_plate: Plate::new(PlateType::Source, PlateFormat::W384), source_plate: Plate::new(PlateType::Source, PlateFormat::W384),
dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384), dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384),
source_region: Region::Rect(Well{ row: 1, col: 1}, Well{ row: 2, col: 3}), source_region: Region::Rect(Well { row: 1, col: 1 }, Well { row: 2, col: 3 }),
dest_region: Region::Rect(Well{ row: 2, col: 2}, Well{ row: 11, col: 16}), dest_region: Region::Rect(Well { row: 2, col: 2 }, Well { row: 11, col: 16 }),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (2, 2), interleave_dest: (2, 2),
}; };
@ -530,36 +548,43 @@ mod tests {
let transfer2_dest = transfer2.get_destination_wells(); let transfer2_dest = transfer2.get_destination_wells();
assert_eq!( assert_eq!(
transfer2_source, transfer2_source,
vec![Well{ row: 1, col: 1}, Well{ row: 1, col: 2}, Well{ row: 1, col: 3}, Well{ row: 2, col: 1}, Well{ row: 2, col: 2}, Well{ row: 2, col: 3}], vec![
Well { row: 1, col: 1 },
Well { row: 1, col: 2 },
Well { row: 1, col: 3 },
Well { row: 2, col: 1 },
Well { row: 2, col: 2 },
Well { row: 2, col: 3 }
],
"Failed type replicate 2 source" "Failed type replicate 2 source"
); );
assert_eq!( assert_eq!(
transfer2_dest, transfer2_dest,
vec![ vec![
Well{ row: 2, col: 2}, Well { row: 2, col: 2 },
Well{ row: 2, col: 8}, Well { row: 2, col: 8 },
Well{ row: 6, col: 2}, Well { row: 6, col: 2 },
Well{ row: 6, col: 8}, Well { row: 6, col: 8 },
Well{ row: 2, col: 4}, Well { row: 2, col: 4 },
Well{ row: 2, col: 10}, Well { row: 2, col: 10 },
Well{ row: 6, col: 4}, Well { row: 6, col: 4 },
Well{ row: 6, col: 10}, Well { row: 6, col: 10 },
Well{ row: 2, col: 6}, Well { row: 2, col: 6 },
Well{ row: 2, col: 12}, Well { row: 2, col: 12 },
Well{ row: 6, col: 6}, Well { row: 6, col: 6 },
Well{ row: 6, col: 12}, Well { row: 6, col: 12 },
Well{ row: 4, col: 2}, Well { row: 4, col: 2 },
Well{ row: 4, col: 8}, Well { row: 4, col: 8 },
Well{ row: 8, col: 2}, Well { row: 8, col: 2 },
Well{ row: 8, col: 8}, Well { row: 8, col: 8 },
Well{ row: 4, col: 4}, Well { row: 4, col: 4 },
Well{ row: 4, col: 10}, Well { row: 4, col: 10 },
Well{ row: 8, col: 4}, Well { row: 8, col: 4 },
Well{ row: 8, col: 10}, Well { row: 8, col: 10 },
Well{ row: 4, col: 6}, Well { row: 4, col: 6 },
Well{ row: 4, col: 12}, Well { row: 4, col: 12 },
Well{ row: 8, col: 6}, Well { row: 8, col: 6 },
Well{ row: 8, col: 12} Well { row: 8, col: 12 }
], ],
"Failed type replicate 2 destination" "Failed type replicate 2 destination"
); );
@ -571,8 +596,8 @@ mod tests {
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: Plate::new(PlateType::Source, PlateFormat::W384), source_plate: Plate::new(PlateType::Source, PlateFormat::W384),
dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384), dest_plate: Plate::new(PlateType::Destination, PlateFormat::W384),
source_region: Region::Rect(Well{ row: 1, col: 4}, Well{ row: 3, col: 7}), source_region: Region::Rect(Well { row: 1, col: 4 }, Well { row: 3, col: 7 }),
dest_region: Region::Point(Well{ row: 1, col: 9}), dest_region: Region::Point(Well { row: 1, col: 9 }),
interleave_source: (1, 1), interleave_source: (1, 1),
interleave_dest: (0, 2), interleave_dest: (0, 2),
}; };
@ -582,17 +607,24 @@ mod tests {
// Skipping source check---it's just 12 wells. // Skipping source check---it's just 12 wells.
assert_eq!( assert_eq!(
transfer1_dest, transfer1_dest,
vec![Well{ row: 1, col: 9}, Well{ row: 1, col: 11}, Well{ row: 1, col: 13}, Well{ row: 1, col: 15}].into_iter().collect(), vec![
Well { row: 1, col: 9 },
Well { row: 1, col: 11 },
Well { row: 1, col: 13 },
Well { row: 1, col: 15 }
]
.into_iter()
.collect(),
"Failed type pool 1 dest" "Failed type pool 1 dest"
); );
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 2, col: 6}), transfer1_map(Well { row: 2, col: 6 }),
Some(vec![Well{ row: 1, col: 13}]), Some(vec![Well { row: 1, col: 13 }]),
"Failed type pool 1 map 1" "Failed type pool 1 map 1"
); );
assert_eq!( assert_eq!(
transfer1_map(Well{ row: 3, col: 7}), transfer1_map(Well { row: 3, col: 7 }),
Some(vec![Well{ row: 1, col: 15}]), Some(vec![Well { row: 1, col: 15 }]),
"Failed type pool 1 map 2" "Failed type pool 1 map 2"
); );
} }