Skip to content

Commit c1887cd

Browse files
committed
get_seed fallback
1 parent 667accf commit c1887cd

6 files changed

Lines changed: 76 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ is roughly based on [Keep a Changelog], and this project tries to adheres to
99

1010
### Changed
1111

12-
- Combined `IterWithContext`, `ReaderWithContext`, and `WriterWithContext` into a single `WithContext` struct
13-
- The default method for `aligner` is now the 3-pass algorithm which has a more stable runtime and memory performance for large sequences. This may result in different optimal alignments
14-
- The profile for alignment is now built from the reference by default when the 1-pass algorithm is enabled. This may result in different optimal alignments
12+
- Combined `IterWithContext`, `ReaderWithContext`, and `WriterWithContext` into
13+
a single `WithContext` struct
14+
- The default method for `aligner` is now the 3-pass algorithm which has a more
15+
stable runtime and memory performance for large sequences. This may result in
16+
different optimal alignments
17+
- The profile for alignment is now built from the reference by default when the
18+
1-pass algorithm is enabled. This may result in different optimal alignments
19+
- `sampler` now uses `IRMA_SEED` as a fallback if no `--rng-seed` is provided,
20+
allowing reproducibility in sampling runs
1521

1622
### Removed
1723

@@ -22,8 +28,11 @@ is roughly based on [Keep a Changelog], and this project tries to adheres to
2228

2329
### Added
2430

25-
- Added preliminary implementations for `FastX` views (within `irma-records` public API)
26-
- A `ValidatePaths` trait is now available in the library portion for ensuring no path is passed as both an input and an output, and that all output paths are distinct
31+
- Added preliminary implementations for `FastX` views (within `irma-records`
32+
public API)
33+
- A `ValidatePaths` trait is now available in the library portion for ensuring
34+
no path is passed as both an input and an output, and that all output paths
35+
are distinct
2736
- `WriteRecord` is now compatible with `FastX`
2837

2938
### Changed

crates/irma-core-cli/processes/shared/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) trait PrintWarning {
2323
/// `stdout` or `stderr`.
2424
///
2525
/// The message includes a timestamp, indentation based on the shell level
26-
/// (`SHLVL` environmental variable), and the word `WARNING`.
26+
/// (`SHLVL` environment variable), and the word `WARNING`.
2727
fn warn(&self, program: &str, message: &str, use_stderr: bool);
2828
}
2929

crates/irma-core-cli/processes/standalone/sampler.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use clap::Args;
44
use irma_records::{
5+
hashing::get_seed,
56
io::{
67
DispatchFastX, FastXReader, FastXType, InputOptions, OutputOptions, ReadFileZipInThread, RecordReaders,
78
RecordWriters, SequenceWriter, ValidatePaths, WithContext, WriteFileZipStdout, WriteRecord,
@@ -48,7 +49,8 @@ pub struct SamplerArgs {
4849

4950
#[arg(short = 's', long)]
5051
/// For reproducibility, provide an optional seed for the random number
51-
/// generator
52+
/// generator. If not provided, checks `IRMA_SEED` environment variable, and
53+
/// finally falls back to a random seed
5254
pub rng_seed: Option<u64>,
5355

5456
#[arg(short = 'v', long)]
@@ -402,6 +404,8 @@ enum SamplingTarget {
402404
fn parse_sampler_args(args: SamplerArgs) -> Result<(IOArgs, Xoshiro256StarStar, SamplingTarget, bool), std::io::Error> {
403405
let rng = if let Some(seed) = &args.rng_seed {
404406
Xoshiro256StarStar::seed_from_u64(*seed)
407+
} else if let Some(seed) = get_seed() {
408+
Xoshiro256StarStar::seed_from_u64(seed)
405409
} else {
406410
make_rng()
407411
};

crates/irma-records/hashing.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,50 @@ use std::env;
33

44
const SEED_ENV_VAR: &str = "IRMA_SEED";
55

6-
fn get_seed() -> Option<u64> {
7-
env::var(SEED_ENV_VAR).ok().map(|s| s.bytes().fold(0, |a, b| a ^ b) as u64)
6+
/// Attempts to parse the environment variable `IRMA_SEED` into a `u64` to use
7+
/// as a seed. If a seed exists but is unable to be parsed, this falls back to a
8+
/// hashing algorithm.
9+
pub fn get_seed() -> Option<u64> {
10+
env::var(SEED_ENV_VAR)
11+
.ok()
12+
.map(|value| value.parse::<u64>().unwrap_or_else(|_| seed_from_string(&value)))
813
}
914

15+
fn seed_from_string(value: &str) -> u64 {
16+
let mut result = [0u8; 8];
17+
18+
let (chunks, remainder) = value.as_bytes().as_chunks::<8>();
19+
// takes the seed in chunks of 8 bytes, and XORs the ith byte of all chunks
20+
// against eachother
21+
for chunk in chunks {
22+
for (position, byte) in chunk.iter().enumerate() {
23+
result[position] ^= byte;
24+
}
25+
}
26+
// handle the bytes in the remainder
27+
for (position, byte) in remainder.iter().enumerate() {
28+
result[position] ^= byte;
29+
}
30+
// then folds the bytes back into a u64 by shifting each byte by 8*i, then
31+
// ORing it against the accumulator
32+
result
33+
.into_iter()
34+
.enumerate()
35+
.fold(0u64, |seed, (position, byte)| seed | (u64::from(byte) << (8 * position)))
36+
}
37+
38+
/// Creates a hasher based on the seed provided in the environment variable
39+
/// `IRMA_SEED`, or falls back to creating a random one.
1040
pub fn get_hasher() -> SeedableRandomState {
1141
match get_seed() {
1242
Some(seed) => SeedableRandomState::with_seed(seed, SharedSeed::global_fixed()),
1343
None => SeedableRandomState::random(),
1444
}
1545
}
46+
47+
#[test]
48+
fn test_seed_from_string() {
49+
// i worked this one out by hand
50+
let string = "ACGTACGTTGCATGCA";
51+
assert_eq!(seed_from_string(string), 1514339863296738325);
52+
}

docs/ALIGNER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ IRMA-core's `aligner` provides an efficient and exact local sequence alignment r
66

77
## Multithreading
88

9-
`aligner` uses `rayon` to perform multithreading to enable higher throughput. To specify the number of threads, set the `RAYON_NUM_THREADS` environmental variable as described [in rayon](https://docs.rs/rayon/latest/rayon/fn.max_num_threads.html). Or, to limit to a single worker thread, pass `--single-thread` to `aligner`.
9+
`aligner` uses `rayon` to perform multithreading to enable higher throughput. To specify the number of threads, set the `RAYON_NUM_THREADS` environment variable as described [in rayon](https://docs.rs/rayon/latest/rayon/fn.max_num_threads.html). Or, to limit to a single worker thread, pass `--single-thread` to `aligner`.
1010

1111
For benchmarking or scenarios where a single thread is always used, the `dev_no_rayon` feature can be enabled in IRMA-core to remove the use of channels. This feature may be removed in future releases, and so should not be relied upon except for testing.
1212

docs/SAMPLER.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,33 @@
44

55
Next generation sequencers often produce an excess of reads, for reasons such as to create redundancy for enabling consensus generation, due to requiring a minimum amount of reagents per run, or other reasons. Running the full read set through a pipeline can be computationally expensive, so it may be desirable to transform the data into a smaller subset to save time.
66

7-
IRMA-core's sampler process provides efficient, random, and fully-representative downsampling (also referred to as subsampling), as well as some other useful functionality.
7+
IRMA-core's `sampler` process provides efficient, random, and fully-representative downsampling (also referred to as subsampling), as well as some other useful functionality.
88

99
## Downsampling Targets
1010

11-
IRMA-core's sampler requires a target for downsampling. This can either be provided as `--subsample-target`, which is the exact number of reads to be in the subsampled output, or `--percent-target`, which is the percentage of the original amount of reads to be in the subsampled output.
11+
IRMA-core's `sampler` requires a target for downsampling. This can either be provided as `--subsample-target`, which is the exact number of reads to be in the subsampled output, or `--percent-target`, which is the percentage of the original amount of reads to be in the subsampled output.
1212

1313
Percent targets must be provided as an integer [0-100] and may not provide an exact percentage downsampled in the cases of streamed or compressed input.
1414

1515
- If a `--percent-target` of 100 is provided, no downsampling will occur. This could be useful for de-interleaving without downsampling.
1616
- If a `--subsample-target` is provided that is *greater* than the amount of sequences in the input, the process will succeed and give an output that is identical to the input, but provide a warning for the user.
1717

18+
## Random number generation and seeds
19+
20+
`sampler` uses the `Xoshiro256StarStar` random number generator, which requires
21+
a seed, which will be accessed in the following order:
22+
23+
1. A seed may be provided via `--rng-seed` in a `u64` format
24+
2. If that `--rng-seed` is not provided, `sampler` will check the `IRMA_SEED`
25+
environment variable, and if it is set, attempt to parse it into a `u64`
26+
3. If `IRMA_SEED` is unable to be parsed, `sampler` will run the seed through a
27+
hashing function
28+
4. Lastly, if `--rng-seed` is not provided, and `IRMA_SEED` is not set, a random
29+
seed will be generated from the system
30+
1831
## Inputs and Outputs
1932

20-
Sampler can downsample `FASTQ` and `FASTA` formats. Inputs are provided as positional arguments, with sampler accepting either a single file, or as a pair of paired-read files. The files may also be a stream (e.g., from a process substitution) or a `.gz` compressed file.
33+
`sampler` can downsample `FASTQ` and `FASTA` formats. Inputs are provided as positional arguments, with `sampler` accepting either a single file, or as a pair of paired-read files. The files may also be a stream (e.g., from a process substitution) or a `.gz` compressed file.
2134

2235
For outputs, you can select one output file with `-o` (`--output`) or two output files with `-1` and `-2` (`--output` and `--output2`). If no output is provided, IRMA-core will output the subsampled data to `stdout`.
2336

0 commit comments

Comments
 (0)