Precompute in calculate_map
Gitea Scan/plate-tool/pipeline/head This commit looks good Details

should help performance a wee bit
This commit is contained in:
Emilia Allison 2024-11-16 22:19:03 -06:00
parent 3d8445ec82
commit f50db96697
Signed by: emilia
GPG Key ID: 05D5D1107E5100A1
1 changed files with 85 additions and 49 deletions

View File

@ -182,61 +182,98 @@ impl TransferRegion {
let Well { row: i, col: j } = w; let Well { row: i, col: j } = w;
if source_wells.contains(&w) { if source_wells.contains(&w) {
let possible_destination_wells = create_dense_rectangle(c1, c2); let possible_destination_wells = create_dense_rectangle(c1, c2);
// Destination upper-left, bottom-right
let (d_ul, d_br) = standardize_rectangle(c1, c2); let (d_ul, d_br) = standardize_rectangle(c1, c2);
// Source upper-left, bottom-right
let (s_ul, s_br) = let (s_ul, s_br) =
standardize_rectangle(&source_corners.0, &source_corners.1); standardize_rectangle(&source_corners.0, &source_corners.1);
// Get base size of source region before interleave
let s_dims = ( let s_dims = (
s_br.row.checked_sub(s_ul.row).unwrap() + 1, s_br.row.checked_sub(s_ul.row).unwrap() + 1,
s_br.col.checked_sub(s_ul.col).unwrap() + 1, s_br.col.checked_sub(s_ul.col).unwrap() + 1,
); );
// Get base size of destination region before interleave
let d_dims = ( let d_dims = (
d_br.row.checked_sub(d_ul.row).unwrap() + 1, d_br.row.checked_sub(d_ul.row).unwrap() + 1,
d_br.col.checked_sub(d_ul.col).unwrap() + 1, d_br.col.checked_sub(d_ul.col).unwrap() + 1,
); );
// Precompute unsigned_abs: this should be cheap but
// this is called constantly in loops, plus prettier this way
let il_dest_uabs = (il_dest.0.unsigned_abs(), il_dest.1.unsigned_abs());
let il_source_uabs = (il_source.0.unsigned_abs(), il_source.1.unsigned_abs());
// Get the actual number of source wells in each dimension
// after considering interleave; essentially squish the air out of the
// region.
let number_used_src_wells = ( let number_used_src_wells = (
// Number of used source wells (s_dims.0 + il_source_uabs.0 - 1)
(s_dims.0 + il_source.0.unsigned_abs() - 1) .div_euclid(il_source_uabs.0),
.div_euclid(il_source.0.unsigned_abs()), (s_dims.1 + il_source_uabs.1 - 1)
(s_dims.1 + il_source.1.unsigned_abs() - 1) .div_euclid(il_source_uabs.1),
.div_euclid(il_source.1.unsigned_abs()),
); );
let count = (
// How many times can we replicate? // How many times can we replicate in each direction?
if il_dest.0.unsigned_abs() == 0 { // Lazy solution: just increment the number of replicates until it wouldn't
// fit anymore!
let count = {
let replicate_base_size = (
number_used_src_wells.0 * il_dest_uabs.0,
number_used_src_wells.1 * il_dest_uabs.1,
);
(
if il_dest_uabs.0 == 0 {
1 1
} else { } else {
(1..) (1..)
.position(|n| { .position(|n| {
(n * number_used_src_wells.0 * il_dest.0.unsigned_abs()) (n * replicate_base_size.0)
.saturating_sub(il_dest.0.unsigned_abs()) .saturating_sub(il_dest_uabs.0)
+ 1 + 1
> d_dims.0 > d_dims.0
}) })
.unwrap() as u8 .unwrap() as u8
}, },
if il_dest.1.unsigned_abs() == 0 { if il_dest_uabs.1 == 0 {
1 1
} else { } else {
(1..) (1..)
.position(|n| { .position(|n| {
(n * number_used_src_wells.1 * il_dest.1.unsigned_abs()) (n * replicate_base_size.1)
.saturating_sub(il_dest.1.unsigned_abs()) .saturating_sub(il_dest_uabs.1)
+ 1 + 1
> d_dims.1 > d_dims.1
}) })
.unwrap() as u8 .unwrap() as u8
}, },
); )
};
// Normalize our i,j (source coords) to a dense rectangle consisting only
// of points actually in the transfer.
let i = i let i = i
.saturating_sub(s_ul.row) .saturating_sub(s_ul.row)
.saturating_div(il_source.0.unsigned_abs()); .saturating_div(il_source_uabs.0);
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_uabs.1);
let checked_il_dest = (u8::max(il_dest.0.unsigned_abs(), 1u8),
u8::max(il_dest.1.unsigned_abs(), 1u8)); // Should not matter because of validation,
// but a cheap way to prevent div by 0
let checked_il_dest = (
u8::max(il_dest_uabs.0, 1u8),
u8::max(il_dest_uabs.1, 1u8),
);
// Precompute multiplication
let row_modulus = number_used_src_wells.0 * checked_il_dest.0; let row_modulus = number_used_src_wells.0 * checked_il_dest.0;
let column_modulus = number_used_src_wells.1 * checked_il_dest.1; let column_modulus = number_used_src_wells.1 * checked_il_dest.1;
let residue_class = (
(il_dest_uabs.0 * i) % row_modulus,
(il_dest_uabs.1 * j) % column_modulus
);
Some( Some(
possible_destination_wells possible_destination_wells
@ -244,24 +281,22 @@ impl TransferRegion {
.filter(|Well { row: x, .. }| { .filter(|Well { row: x, .. }| {
x.checked_sub(d_ul.row).unwrap() x.checked_sub(d_ul.row).unwrap()
% row_modulus // Counter along x % row_modulus // Counter along x
== (il_dest.0.unsigned_abs() *i) == residue_class.0
% row_modulus
}) })
.filter(|Well { col: y, .. }| { .filter(|Well { col: y, .. }| {
y.checked_sub(d_ul.col).unwrap() y.checked_sub(d_ul.col).unwrap()
% column_modulus // Counter along u % column_modulus // Counter along u
== (il_dest.1.unsigned_abs() *j) == residue_class.1
% 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(row_modulus)
row_modulus < count.0
) < count.0 && y.checked_sub(d_ul.col)
&& y.checked_sub(d_ul.col).unwrap().div_euclid( .unwrap()
column_modulus .div_euclid(column_modulus)
) < count.1 < count.1
}) })
.collect(), .collect(),
) )
@ -320,7 +355,7 @@ impl TransferRegion {
} }
if il_dest == (0, 0) { if il_dest == (0, 0) {
return Err("Refusing to pool both dimensions in a rectangular transfer!\nPlease select a point in the destination plate.") 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(()),
@ -343,7 +378,8 @@ fn create_dense_rectangle(c1: &Well, c2: &Well) -> Vec<Well> {
// Creates a vector of every point between two corners // Creates a vector of every point between two corners
let (c1, c2) = standardize_rectangle(c1, c2); let (c1, c2) = standardize_rectangle(c1, c2);
let mut points = Vec::<Well>::new(); let number_wells: usize = ((c2.row - c1.row + 1) as usize) * ((c2.col - c1.col + 1) as usize);
let mut points = Vec::<Well>::with_capacity(number_wells);
for i in c1.row..=c2.row { for i in c1.row..=c2.row {
for j in c1.col..=c2.col { for j in c1.col..=c2.col {
points.push(Well { row: i, col: j }); points.push(Well { row: i, col: j });