Fix replicate with destination interleave

This commit is contained in:
Emilia Allison 2023-05-25 11:43:01 -04:00
parent 74fa50bc0c
commit a1a0430ec3
Signed by: emilia
GPG Key ID: 7A3F8997BFE894E0
3 changed files with 83 additions and 51 deletions

View File

@ -75,11 +75,9 @@ pub fn TransferMenu() -> Html {
dispatch.reduce_mut(|state| { dispatch.reduce_mut(|state| {
state.interleave_x = num; state.interleave_x = num;
}); });
if let Some((_,y)) = ct_state.transfer.interleave_dest { ct_dispatch.reduce_mut(|state| {
ct_dispatch.reduce_mut(|state| { state.transfer.interleave_dest = (num, state.transfer.interleave_dest.1);
state.transfer.interleave_dest = Some((num, y)); });
});
}
} }
} }
}) })
@ -97,11 +95,9 @@ pub fn TransferMenu() -> Html {
dispatch.reduce_mut(|state| { dispatch.reduce_mut(|state| {
state.interleave_y = num; state.interleave_y = num;
}); });
if let Some((x,_)) = ct_state.transfer.interleave_dest { ct_dispatch.reduce_mut(|state| {
ct_dispatch.reduce_mut(|state| { state.transfer.interleave_dest = (state.transfer.interleave_dest.0, num);
state.transfer.interleave_dest = Some((x, num)); });
});
}
} }
} }
}) })

View File

@ -24,14 +24,27 @@ impl TryFrom<Region> for ((u8, u8), (u8, u8)) {
} }
} }
#[derive(PartialEq, Eq, Default, Clone, Copy, Serialize, Deserialize, Debug)] #[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Debug)]
pub struct TransferRegion { pub struct TransferRegion {
pub source_plate: Plate, pub source_plate: Plate,
pub source_region: Region, // Even if it is just a point, we don't want corners. pub source_region: Region, // Even if it is just a point, we don't want corners.
pub dest_plate: Plate, pub dest_plate: Plate,
pub dest_region: Region, pub dest_region: Region,
pub interleave_source: Option<(i8, i8)>, pub interleave_source: (i8, i8),
pub interleave_dest: Option<(i8, i8)>, pub interleave_dest: (i8, i8),
}
impl Default for TransferRegion {
fn default() -> Self {
TransferRegion {
source_plate: Plate::default(),
source_region: Region::default(),
dest_plate: Plate::default(),
dest_region: Region::default(),
interleave_source: (1,1),
interleave_dest: (1,1)
}
}
} }
impl TransferRegion { impl TransferRegion {
@ -40,7 +53,7 @@ impl TransferRegion {
Region::Rect(c1, c2) => { Region::Rect(c1, c2) => {
let mut wells = Vec::<(u8, u8)>::new(); let mut wells = Vec::<(u8, u8)>::new();
let (ul, br) = standardize_rectangle(&c1, &c2); let (ul, br) = standardize_rectangle(&c1, &c2);
let (interleave_i, interleave_j) = self.interleave_source.unwrap_or((1, 1)); let (interleave_i, interleave_j) = self.interleave_source;
// NOTE: This will panic if either is 0! // NOTE: This will panic if either is 0!
// We'll reassign these values (still not mutable) just in case. // We'll reassign these values (still not mutable) just in case.
// This behaviour shouldn't be replicated for destination wells // This behaviour shouldn't be replicated for destination wells
@ -82,16 +95,17 @@ impl TransferRegion {
pub fn calculate_map(&self) -> Box<dyn Fn((u8, u8)) -> Option<Vec<(u8, u8)>> + '_> { pub fn calculate_map(&self) -> Box<dyn Fn((u8, u8)) -> Option<Vec<(u8, u8)>> + '_> {
// 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); eprintln!("{}", msg);
log::debug!("{}", msg);
eprintln!("This transfer will be empty."); eprintln!("This transfer will be empty.");
return Box::new(|(_, _)| None); return Box::new(|(_, _)| None);
} }
log::debug!("What is ild? {:?}", self);
let source_wells = self.get_source_wells(); let source_wells = self.get_source_wells();
let il_dest = self.interleave_dest.unwrap_or((1, 1)); let il_dest = self.interleave_dest;
let il_source = self.interleave_source.unwrap_or((1, 1)); let il_source = self.interleave_source;
let source_corners: ((u8, u8), (u8, u8)) = match self.source_region { let source_corners: ((u8, u8), (u8, u8)) = match self.source_region {
Region::Point((x,y)) => ((x,y),(x,y)), Region::Point((x,y)) => ((x,y),(x,y)),
@ -130,32 +144,56 @@ impl TransferRegion {
} }
Region::Rect(c1, c2) => { Region::Rect(c1, c2) => {
return Box::new(move |(i, j)| { return Box::new(move |(i, j)| {
// Because of our call to validate,
// we can assume that our destination region contains
// an integer number of our source regions.
if source_wells.contains(&(i, j)) { if source_wells.contains(&(i, j)) {
// Find points by checking congruence class
let possible_destination_wells = create_dense_rectangle(&c1, &c2); let possible_destination_wells = create_dense_rectangle(&c1, &c2);
let (ds1, _) = standardize_rectangle(&c1, &c2); let (d_ul, d_br) = standardize_rectangle(&c1, &c2);
let (s1, s2) = standardize_rectangle(&source_corners.0, &source_corners.1); let (s_ul, s_br) = standardize_rectangle(&source_corners.0, &source_corners.1);
let dims = ( let s_dims = (
s2.0.checked_sub(s1.0).unwrap() + 1, s_br.0.checked_sub(s_ul.0).unwrap() + 1,
s2.1.checked_sub(s1.1).unwrap() + 1, s_br.1.checked_sub(s_ul.1).unwrap() + 1,
);
let d_dims = (
d_br.0.checked_sub(d_ul.0).unwrap() + 1,
d_br.1.checked_sub(d_ul.1).unwrap() + 1,
);
let N_s = ( // Number of used source wells
s_dims.0.div_euclid(il_source.0.abs() as u8),
s_dims.1.div_euclid(il_source.1.abs() as u8),
);
let D_per_replicate = ( // How many wells are used per replicate?
(s_dims.0 * (il_dest.0.abs() as u8))
// Conditionally subtract one to ignore the trailing interleave well
.saturating_sub(if il_dest.0.abs() > 1 {1} else {0}),
(s_dims.1 * (il_dest.1.abs() as u8))
.saturating_sub(if il_dest.1.abs() > 1 {1} else {0}),
);
let count = ( // How many times can we replicate?
d_dims.0.div_euclid(D_per_replicate.0),
d_dims.1.div_euclid(D_per_replicate.1),
); );
Some( Some(
possible_destination_wells possible_destination_wells
.into_iter() .into_iter()
.filter(|(x, y)| { .filter(|(x, _)| {
i * il_dest.0.abs() as u8 % dims.0 x.checked_sub(d_ul.0).unwrap()
== (x.checked_sub(ds1.0).unwrap() + 1) % dims.0 % (N_s.0 * il_dest.0.abs() as u8) // Counter along x
&& j * il_dest.1.abs() as u8 % dims.1 == ((il_dest.0.abs() as u8 *(i).checked_sub(1u8).unwrap()))
== (y.checked_sub(ds1.1).unwrap() + 1) % dims.1 % (N_s.0 * il_dest.0.abs() as u8)
}) })
.filter(|(x, y)| { .filter(|(_, y)| {
(x.checked_sub(ds1.0).unwrap()) % il_dest.0.abs() as u8 == 0 y.checked_sub(d_ul.1).unwrap()
&& (y.checked_sub(ds1.1).unwrap()) % il_dest.1.abs() as u8 % (N_s.1 * il_dest.1.abs() as u8) // Counter along u
== 0 == ((il_dest.1.abs() as u8 *(j).checked_sub(1u8).unwrap()))
% (N_s.1 * il_dest.1.abs() as u8)
})
.filter(|(x,y)| {
// How many times have we replicated? < How many are we allowed
// to replicate?
x.checked_sub(d_ul.0).unwrap().div_euclid(N_s.0 * il_dest.0.abs() as u8)
< count.0 &&
y.checked_sub(d_ul.1).unwrap().div_euclid(N_s.1 * il_dest.1.abs() as u8)
< count.1
}) })
.collect(), .collect(),
) )
@ -176,8 +214,8 @@ impl TransferRegion {
// - Are the wells in the source really there? // - Are the wells in the source really there?
// - In a replication region, do the source lengths divide the destination lengths? // - In a replication region, do the source lengths divide the destination lengths?
// - Are the interleaves valid? // - Are the interleaves valid?
let il_source = self.interleave_source.unwrap_or((1, 1)); let il_source = self.interleave_source;
let il_dest = self.interleave_dest.unwrap_or((1, 1)); let il_dest = self.interleave_dest;
match self.source_region { match self.source_region {
Region::Point(_) => return Ok(()), // Should make sure it's actually in the plate, leave for Region::Point(_) => return Ok(()), // Should make sure it's actually in the plate, leave for
@ -233,10 +271,8 @@ impl TransferRegion {
} }
} }
if let Some(source_il) = self.interleave_source { if il_source.0 == 0 || il_dest.1 == 0 {
if source_il.0 == 0 || source_il.1 == 0 { return Err("Source interleave cannot be zero!");
return Err("Source interleave cannot be zero!");
}
} }
// Check if all destination wells exist: // Check if all destination wells exist:
@ -341,8 +377,8 @@ mod tests {
source_region: Region::Rect((1, 1), (3, 3)), source_region: Region::Rect((1, 1), (3, 3)),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point((3,3)), dest_region: Region::Point((3,3)),
interleave_source: None, interleave_source: (1,1),
interleave_dest: None, interleave_dest: (1,1),
}; };
let transfer1_map = transfer1.calculate_map(); let transfer1_map = transfer1.calculate_map();
assert_eq!(transfer1_map((1,1)), Some(vec!{(3,3)}), "Failed basic shift transfer 1"); assert_eq!(transfer1_map((1,1)), Some(vec!{(3,3)}), "Failed basic shift transfer 1");
@ -354,8 +390,8 @@ mod tests {
source_region: Region::Rect((1, 1), (3, 3)), source_region: Region::Rect((1, 1), (3, 3)),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point((3,3)), dest_region: Region::Point((3,3)),
interleave_source: Some((2,2)), interleave_source: (2,2),
interleave_dest: None, interleave_dest: (1,1),
}; };
let transfer2_map = transfer2.calculate_map(); let transfer2_map = transfer2.calculate_map();
assert_eq!(transfer2_map((1,1)), Some(vec!{(3,3)}), "Failed source interleave, type simple 1"); assert_eq!(transfer2_map((1,1)), Some(vec!{(3,3)}), "Failed source interleave, type simple 1");
@ -368,8 +404,8 @@ mod tests {
source_region: Region::Rect((1, 1), (3, 3)), source_region: Region::Rect((1, 1), (3, 3)),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Point((3,3)), dest_region: Region::Point((3,3)),
interleave_source: None, interleave_source: (1,1),
interleave_dest: Some((2,3)), interleave_dest: (2,3),
}; };
let transfer3_map = transfer3.calculate_map(); let transfer3_map = transfer3.calculate_map();
assert_eq!(transfer3_map((1,1)), Some(vec!{(3,3)}), "Failed destination interleave, type simple 1"); assert_eq!(transfer3_map((1,1)), Some(vec!{(3,3)}), "Failed destination interleave, type simple 1");
@ -388,8 +424,8 @@ mod tests {
source_region: Region::Rect((1, 1), (2, 2)), source_region: Region::Rect((1, 1), (2, 2)),
dest_plate: destination, dest_plate: destination,
dest_region: Region::Rect((2,2),(11,11)), dest_region: Region::Rect((2,2),(11,11)),
interleave_source: None, interleave_source: (1,1),
interleave_dest: Some((3,3)), interleave_dest: (3,3),
}; };
let transfer1_map = transfer1.calculate_map(); let transfer1_map = transfer1.calculate_map();
assert_eq!(transfer1_map((1,1)), Some(vec!{(2, 2), (2, 8), (8, 2), (8, 8)}), "Failed type replicate 1"); assert_eq!(transfer1_map((1,1)), Some(vec!{(2, 2), (2, 8), (8, 2), (8, 8)}), "Failed type replicate 1");

View File

@ -25,8 +25,8 @@ pub fn plate_test() {
source_region: transfer_region::Region::Rect((1, 1), (2, 2)), source_region: transfer_region::Region::Rect((1, 1), (2, 2)),
dest_plate: destination, dest_plate: destination,
dest_region: transfer_region::Region::Rect((2,2),(11,11)), dest_region: transfer_region::Region::Rect((2,2),(11,11)),
interleave_source: None, interleave_source: (1,1),
interleave_dest: Some((3,3)), interleave_dest: (3,3),
}; };
println!("{}", transfer); println!("{}", transfer);
let sws = transfer.get_source_wells(); let sws = transfer.get_source_wells();