Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions ext/src/chain_complex/chain_homotopy.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::chain_complex::{ChainComplex, FreeChainComplex};
use crate::resolution_homomorphism::ResolutionHomomorphism;
use crate::save::SaveKind;
use crate::{
chain_complex::{ChainComplex, FreeChainComplex},
save::SaveOption,
};
use algebra::module::homomorphism::{FreeModuleHomomorphism, ModuleHomomorphism};
use algebra::module::Module;
use fp::prime::ValidPrime;
Expand Down Expand Up @@ -31,6 +34,7 @@ pub struct ChainHomotopy<
/// Homotopies, indexed by the filtration of the target of f - g.
homotopies: OnceBiVec<Arc<FreeModuleHomomorphism<U::Module>>>,
save_dir: Option<PathBuf>,
save_option: SaveOption,
}

impl<
Expand All @@ -43,14 +47,29 @@ impl<
left: Arc<ResolutionHomomorphism<S, T>>,
right: Arc<ResolutionHomomorphism<T, U>>,
) -> Self {
Self::new_with_save_option(left, right, Default::default())
}

pub fn new_with_save_option(
left: Arc<ResolutionHomomorphism<S, T>>,
right: Arc<ResolutionHomomorphism<T, U>>,
save_option: SaveOption,
) -> Self {
if save_option.required() {
assert!(
left.source.save_dir().is_some(),
"Saving is required but no path was provided"
);
}
let save_dir = if left.source.save_dir().is_some()
&& !left.name().is_empty()
&& !right.name().is_empty()
{
let mut path = left.source.save_dir().unwrap().to_owned();
path.push(format!("massey/{},{}/", left.name(), right.name(),));

SaveKind::ChainHomotopy.create_dir(&path).unwrap();
if save_option.write() {
path.push(format!("massey/{},{}/", left.name(), right.name(),));
SaveKind::ChainHomotopy.create_dir(&path).unwrap();
}

Some(path)
} else {
Expand All @@ -64,6 +83,7 @@ impl<
right,
lock: Mutex::new(()),
save_dir,
save_option,
}
}

Expand Down Expand Up @@ -282,7 +302,8 @@ impl<
&scratches,
));

if let Some(dir) = &self.save_dir {
if self.save_option.write() && self.save_dir.is_some() {
let dir = self.save_dir.as_ref().unwrap();
let mut f = self
.left
.source
Expand Down
92 changes: 70 additions & 22 deletions ext/src/resolution_homomorphism.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;

use crate::chain_complex::{
AugmentedChainComplex, BoundedChainComplex, ChainComplex, FreeChainComplex,
};
use crate::save::SaveKind;
use crate::{
chain_complex::{AugmentedChainComplex, BoundedChainComplex, ChainComplex, FreeChainComplex},
save::SaveOption,
};
use algebra::module::homomorphism::{ModuleHomomorphism, MuFreeModuleHomomorphism};
use algebra::module::Module;
use algebra::MuAlgebra;
Expand Down Expand Up @@ -38,6 +39,7 @@ where
pub shift_s: u32,
pub shift_t: i32,
save_dir: Option<PathBuf>,
save_option: SaveOption,
}

impl<const U: bool, CC1, CC2> MuResolutionHomomorphism<U, CC1, CC2>
Expand All @@ -53,10 +55,30 @@ where
shift_s: u32,
shift_t: i32,
) -> Self {
Self::new_with_save_option(name, source, target, shift_s, shift_t, Default::default())
}

pub fn new_with_save_option(
name: String,
source: Arc<CC1>,
target: Arc<CC2>,
shift_s: u32,
shift_t: i32,
save_option: SaveOption,
) -> Self {
if save_option.required() {
assert!(
source.save_dir().is_some(),
"Saving is required but no path was provided"
);
}

let save_dir = if source.save_dir().is_some() && !name.is_empty() {
let mut path = source.save_dir().unwrap().to_owned();
path.push(format!("products/{name}"));
SaveKind::ChainMap.create_dir(&path).unwrap();
if save_option.write() {
path.push(format!("products/{name}"));
SaveKind::ChainMap.create_dir(&path).unwrap();
}
Some(path)
} else {
None
Expand All @@ -70,6 +92,7 @@ where
shift_s,
shift_t,
save_dir,
save_option,
}
}

Expand Down Expand Up @@ -257,14 +280,16 @@ where
let outputs =
extra_images.unwrap_or_else(|| vec![FpVector::new(p, fx_dimension); num_gens]);

if let Some(dir) = &self.save_dir {
let mut f = self
.source
.save_file(SaveKind::ChainMap, input_s, input_t)
.create_file(dir.clone(), false);
f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
for row in &outputs {
row.to_bytes(&mut f).unwrap();
if self.save_option.write() {
if let Some(dir) = &self.save_dir {
let mut f = self
.source
.save_file(SaveKind::ChainMap, input_s, input_t)
.create_file(dir.clone(), false);
f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
for row in &outputs {
row.to_bytes(&mut f).unwrap();
}
}
}

Expand Down Expand Up @@ -335,14 +360,16 @@ where
));
}

if let Some(dir) = &self.save_dir {
let mut f = self
.source
.save_file(SaveKind::ChainMap, input_s, input_t)
.create_file(dir.clone(), false);
f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
for row in &outputs {
row.to_bytes(&mut f).unwrap();
if self.save_option.write() {
if let Some(dir) = &self.save_dir {
let mut f = self
.source
.save_file(SaveKind::ChainMap, input_s, input_t)
.create_file(dir.clone(), false);
f.write_u64::<LittleEndian>(fx_dimension as u64).unwrap();
for row in &outputs {
row.to_bytes(&mut f).unwrap();
}
}
}
f_cur.add_generators_from_rows_ooo(input_t, outputs)
Expand All @@ -363,7 +390,28 @@ where
shift_t: i32,
class: &[u32],
) -> Self {
let result = Self::new(name, source, target, shift_s, shift_t);
Self::from_class_with_save_option(
name,
source,
target,
shift_s,
shift_t,
class,
Default::default(),
)
}

pub fn from_class_with_save_option(
name: String,
source: Arc<CC1>,
target: Arc<CC2>,
shift_s: u32,
shift_t: i32,
class: &[u32],
save_option: SaveOption,
) -> Self {
let result =
Self::new_with_save_option(name, source, target, shift_s, shift_t, save_option);

let num_gens = result
.source
Expand Down
47 changes: 47 additions & 0 deletions ext/src/save.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,50 @@ impl<A: Algebra> SaveFile<A> {
f
}
}

/// Decides whether the structure will be stored to disk.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum SaveOption {
/// Save to disk if a save path was provided.
#[default]
Yes,
/// Do not save to disk even if a save path was provided.
No,
/// Save to disk. Not providing a save path will cause an error.
Force,
/// Only read from disk if the structure has already been computed, but don't write. Note that
/// the directories will still be created if they don't already exist, but they won't be
/// populated.
ReadOnly,
}

impl SaveOption {
pub fn required(&self) -> bool {
matches!(self, SaveOption::Force)
}

pub fn write(&self) -> bool {
match self {
SaveOption::Yes => true,
SaveOption::No => false,
SaveOption::Force => true,
SaveOption::ReadOnly => false,
}
}
}

impl std::str::FromStr for SaveOption {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"" | "default" => Ok(Default::default()),
"yes" | "y" | "Y" => Ok(SaveOption::Yes),
"no" | "n" | "N" => Ok(SaveOption::No),
"force" | "f" | "F" => Ok(SaveOption::Force),
"readonly" | "r" | "ro" | "R" | "RO" => Ok(SaveOption::ReadOnly),
_ => Err(anyhow::anyhow!("Invalid save option")),
}
}
}
83 changes: 83 additions & 0 deletions ext/tests/save_load_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,86 @@ fn test_checksum() {
.unwrap()
.compute_through_bidegree(2, 2);
}

mod save_option {
use std::sync::Arc;

use ext::{
chain_complex::ChainHomotopy, resolution_homomorphism::ResolutionHomomorphism,
save::SaveOption, utils::construct,
};
use rstest::rstest;

use super::lock_tempdir;

#[rstest]
#[case(SaveOption::No)]
#[case(SaveOption::ReadOnly)]
fn test_morphism_locked(#[case] save_option: SaveOption) {
let tempdir = tempfile::TempDir::new().unwrap();

let resolution = Arc::new(construct("S_2", Some(tempdir.path().into())).unwrap());
resolution.compute_through_stem(1, 0);

lock_tempdir(tempdir.path());
ResolutionHomomorphism::from_class_with_save_option(
"h0".into(),
Arc::clone(&resolution),
Arc::clone(&resolution),
1,
1,
&[1],
save_option,
)
.extend_all();
}

#[rstest]
#[case(SaveOption::No)]
#[case(SaveOption::ReadOnly)]
fn test_homotopy_locked(#[case] save_option: SaveOption) {
let tempdir = tempfile::TempDir::new().unwrap();

let resolution = Arc::new(construct("S_2", Some(tempdir.path().into())).unwrap());
resolution.compute_through_stem(2, 2);

let h0 = ResolutionHomomorphism::from_class(
"h0".into(),
Arc::clone(&resolution),
Arc::clone(&resolution),
1,
1,
&[1],
);
h0.extend_all();
let h1 = ResolutionHomomorphism::from_class(
"h1".into(),
Arc::clone(&resolution),
Arc::clone(&resolution),
1,
2,
&[1],
);
h1.extend_all();

lock_tempdir(tempdir.path());
ChainHomotopy::new_with_save_option(Arc::new(h0), Arc::new(h1), save_option).extend_all();
}

#[test]
#[should_panic]
fn test_force() {
let resolution = Arc::new(construct("S_2", None).unwrap());
resolution.compute_through_stem(1, 0);

ResolutionHomomorphism::from_class_with_save_option(
"h0".into(),
Arc::clone(&resolution),
Arc::clone(&resolution),
1,
1,
&[1],
SaveOption::Force,
);
}
}