Skip to content
Snippets Groups Projects
Verified Commit 9cb91f1c authored by STEVAN Antoine's avatar STEVAN Antoine :crab:
Browse files

move inbreeding to `dragoon/nc-diversity`

parent ff5752a1
No related branches found
No related tags found
1 merge request!150move inbreeding to `dragoon/nc-diversity`
Pipeline #5430 passed
Showing
with 0 additions and 1000 deletions
...@@ -23,7 +23,6 @@ rand = "0.8.5" ...@@ -23,7 +23,6 @@ rand = "0.8.5"
[workspace] [workspace]
members = [ members = [
"benchmarks", "benchmarks",
"bins/inbreeding",
"bins/rank", "bins/rank",
] ]
......
[package]
name = "inbreeding"
version = "0.1.0"
edition = "2021"
description = "Study the 'inbreeding' phenomenon in a system supporting 'recoding'."
[dependencies]
komodo = { version = "0.2.0", path = "../.." }
ark-pallas = "0.4.0"
clap = { version = "4.5.4", features = ["derive"] }
rand = "0.8.5"
indicatif = "0.17.8"
ark-ff = "0.4.2"
hex = "0.4.3"
- build the example for best performance with [`inbreeding build`](./src/.nushell/build.nu)
- run the experiment with [`inbreeding run`](./src/.nushell/run.nu)
- plot the results with [`inbreeding plot`](./src/.nushell/plot.nu)
# Example
see [run](scripts/run.nu)
see [plot](scripts/plot.nu)
export use src/.nushell/build.nu
export use src/.nushell/watch.nu
export use src/.nushell/run.nu
export use src/.nushell/inspect.nu
export use src/.nushell/list.nu
export use src/.nushell/load.nu
export use src/.nushell/plot.nu
use ../src/.nushell/consts.nu CACHE
use ../
const FIGURES_DIR = ($CACHE | path join figures)
mkdir $FIGURES_DIR
for exp in (inbreeding list) {
let img = $FIGURES_DIR | path join $exp | path parse --extension '' | update extension "png" | path join
inbreeding load $exp | inbreeding plot --save $img
}
use ../
const OPTS = {
nb_bytes: (10 * 1_024),
k: 10,
n: null,
nb_measurements: 100,
nb_scenarii: 100,
measurement_schedule: 1,
measurement_schedule_start: 0,
max_t: 150,
strategies: [
"single:1",
"single:2",
"single:3"
"single:4"
"single:5",
"single:6",
"single:7",
"single:8",
"single:9",
"single:10",
],
environment: null,
}
const RHOS = [ (1 / 2), (1 / 3), (1 / 5), (1 / 10) ]
const ENVS = [
"fixed:0",
"random-fixed:0.1:1",
"random-fixed:0.2:1",
"random-fixed:0.3:1",
"random-fixed:0.4:1",
"random-fixed:0.5:1",
"random-fixed:0.6:1",
"random-fixed:0.7:1",
"random-fixed:0.8:1",
"random-fixed:0.9:1",
"fixed:1",
]
def prod [a: list, b: list]: [ nothing -> list<list> ] {
$a | each { |ia| $b | each { |ib| [$ia, $ib] }} | flatten
}
def main [
--nb-threads: int = 8,
--prng-seed: string = "0000000000000000000000000000000000000000000000000000000000000000",
] {
inbreeding build
prod $RHOS $ENVS | par-each --threads $nb_threads { |exp|
let opts = $OPTS | update n ($OPTS.k / $exp.0 | math round --precision 0) | update environment $exp.1
inbreeding run --options $opts --prng-seed $prng_seed
}
}
use ../src/.nushell/consts.nu CACHE
use ../src/.nushell/path.nu [ "remove-cache-prefix" ]
use ../src/.nushell/parse.nu [ "parse full-experiment" ]
const SEED = "b239e48345ac457b492cc164f58c010d07292c88e4791e607d91796baec7f334"
let k = 10
# # Example
# ``nushell
# # sort files based on their file extensions
# ls | where type == file | sort-by-closure { get name | path parse | get extension }
# ``
# ---
# ``
# ─┬───────────────────
# 0│LICENSE
# 1│Cargo.lock
# 2│CODE_OF_CONDUCT.md
# 3│CONTRIBUTING.md
# 4│README.md
# 5│toolkit.nu
# 6│Cargo.toml
# 7│Cross.toml
# 8│rust-toolchain.toml
# 9│typos.toml
# ─┴───────────────────
# ``
def sort-by-closure [key: closure]: list -> list {
insert __key__ { do $key $in } | sort-by __key__ | reject __key__
}
def show [x: string] {
$CACHE
| path join figures $"($SEED)-($x).png"
| into glob
| ls $in
| sort-by-closure {
get name
| remove-cache-prefix
| path parse --extension 'png'
| reject extension
| path join
| parse full-experiment
| get n
}
| feh ...$in.name
}
show $"fixed:0-($k)-*-*"
show $"random-fixed:0.5:1-($k)-*-*"
show $"fixed:1-($k)-*-*"
show $"*-($k)-20-*"
show $"*-($k)-30-*"
show $"*-($k)-50-*"
show $"*-($k)-100-*"
export def main [] {
cd bins/inbreeding
cargo build --release
}
export const BIN = "./target/release/inbreeding"
export const CACHE = ($nu.home-path | path join .cache komodo inbreeding)
export const NUSHELL = "../../../../.nushell"
use consts.nu
use parse.nu [ "parse full-experiment" ]
use path.nu [ "remove-cache-prefix" ]
def get-seeds [] [ nothing -> list<string> ] {
$consts.CACHE | path join '*' | into glob | ls $in | get name | each { path split | last }
}
export def main [seed: string@get-seeds]: [
nothing -> table<
seed: string,
env: string,
strategy: string,
k: int,
n: int,
nb_bytes: int,
m: int,
>
] {
$consts.CACHE
| path join ($seed | into string) '*'
| into glob
| ls $in
| insert m { ls $in.name | length }
| select name m
| update name { remove-cache-prefix | parse full-experiment | reject seed }
| flatten --all name
| into int k
| into int n
| into int nb_bytes
}
use consts.nu
use parse.nu [ "parse full-experiment" ]
use path.nu [ "remove-cache-prefix" ]
# return experiment names following `$ARG_EXPERIMENT_FORMAT`
export def main []: nothing -> list<string> {
$consts.CACHE
| path join '*' '*'
| into glob
| ls $in
| get name
| each { remove-cache-prefix }
| where ($it | path split | first | str length) == 64
| each { parse full-experiment | reject strategy | values | str join '-' }
| uniq
}
use consts.nu
use parse.nu [ "parse arg-experiment", "parse experiment" ]
use path.nu [ "remove-cache-prefix" ]
use $consts.NUSHELL error "error throw"
use list.nu
export def main [
experiment: string@list,
]: [
nothing -> record<
experiment: record<k: int, n: int, nb_bytes: int, env: string>,
measurements: table<strategy: string, diversity: table<x: int, y: float, e: float>>,
>
] {
let exp = $experiment | parse arg-experiment --span (metadata $experiment).span
let experiment_path = [
$consts.CACHE,
$exp.seed,
([$exp.env, '*', $exp.k, $exp.n, $exp.nb_bytes] | str join '-')
]
| path join
| into glob
let experiment_files = try {
ls $experiment_path
} catch {
error throw {
err: "invalid experiment",
label: $"experiment '($experiment)' does not appear to have data files",
span: (metadata $experiment).span,
}
}
let measurements = $experiment_files
| select name
| insert . { get name | remove-cache-prefix | parse experiment }
| flatten --all
| insert diversity {
ls $in.name
| each { get name | open | lines }
| flatten
| parse "{x}, {y}"
| into float y
| group-by x --to-table
| insert y { get items.y | math avg }
| insert e { get items.y | math stddev }
| rename --column { group: "x" }
| reject items
| into int x # NOTE: $.x needs to be converted to int here because
# `group-by --to-table` converts the grouping key to
# string
}
| reject name
| group-by strategy --to-table
| update items {|it|
let d = $it.items.diversity
$d | skip 1 | reduce --fold $d.0 {|it, acc| $acc | append $it}
}
| rename --column { group: "strategy", items: "diversity" }
{
experiment: {
env: $exp.env,
k: $exp.k,
n: $exp.n,
nb_bytes: $exp.nb_bytes,
},
measurements: $measurements,
}
}
const FMT = {
env: "(?<env>.*)",
seed: "(?<seed>[a-zA-Z0-9]*)",
params: '(?<k>\d+)-(?<n>\d+)-(?<nb_bytes>\d+)',
strat: "(?<strategy>.*)" ,
}
const ARG_EXPERIMENT_FORMAT = $FMT.seed + '-' + $FMT.env + '-' + $FMT.params
const EXPERIMENT_FORMAT = $FMT.env + '-' + $FMT.strat + '-' + $FMT.params
const FULL_EXPERIMENT_FORMAT = $FMT.seed + (char path_sep) + $EXPERIMENT_FORMAT
export def "parse full-experiment" []: [
string -> record<
seed: string, env: string, strategy: string, k: int, n: int, nb_bytes: int
>
] {
parse --regex $FULL_EXPERIMENT_FORMAT
| into record
| into int k
| into int n
| into int nb_bytes
}
export def "parse experiment" []: [
string -> record<env: string, strategy: string, k: int, n: int, nb_bytes: int>
] {
parse --regex $EXPERIMENT_FORMAT
| into record
| into int k
| into int n
| into int nb_bytes
}
export def "parse arg-experiment" [--span: record<start: int, end: int>]: [
string -> record<seed: string, env: string, k: int, n: int, nb_bytes: int>
] {
let exp = $in
| parse --regex $ARG_EXPERIMENT_FORMAT
| into record
| into int k
| into int n
| into int nb_bytes
if $exp == {} {
error throw {
err: "invalid experiment",
label: $"should have format '($ARG_EXPERIMENT_FORMAT)', found ($experiment)",
span: $span,
}
}
$exp
}
use consts.nu
export def remove-cache-prefix []: path -> string {
str replace $"($consts.CACHE)(char path_sep)" ''
}
use std repeat
use consts.nu
use $consts.NUSHELL plot gplt
use $consts.NUSHELL color *
use $consts.NUSHELL error "error throw"
def "parse strategy" []: string -> record<type: string> {
let s = $in
if ($s | str starts-with "single") {
let res = $s
| parse "single:{n}"
| into record
| into int n
{ type: "single", n: $res.n }
} else {
let res = $s
| parse "double:{p}:{n}:{m}"
| into record
| into float p
| into int n
| into int m
{ type: "double", p: $res.p, n: $res.n, m: $res.m }
}
}
def get-color []: int -> string {
match $in {
10 => "#d62728",
9 => "#ff7f0e",
8 => "#bcbd22",
7 => "#1f77b4",
6 => "#9467bd",
5 => "#2ca02c",
4 => "#17becf",
3 => "#8c564b",
2 => "#e377c2",
_ => "#7f7f7f",
}
}
export def main [ --save: path ]: [
record<
experiment: record<k: int, n: int, nb_bytes: int, env: string>,
measurements: table<strategy: string, diversity: table<x: int, y: float, e: float>>,
> -> nothing
] {
let experiment = $in
let l = $experiment.measurements.diversity.0 | length
$experiment.measurements
| update strategy { parse strategy }
| insert sort {|it|
match $it.strategy.type {
"single" => [$it.strategy.n, 1.0]
"double" => [$it.strategy.n, $it.strategy.p]
}
}
| sort-by sort
| reverse
| reject sort
| insert name {|it|
match $it.strategy.type {
"single" => {
let sigma = if $it.strategy.n == $experiment.experiment.k {
"k"
} else {
$"k - ($experiment.experiment.k - $it.strategy.n)"
}
$"$\\sigma = ($sigma) = ($it.strategy.n)$"
}
"double" => $"($it.strategy.p)? ($it.strategy.n) ; ($it.strategy.m)"
}
}
| rename --column { diversity: "points" }
| insert style {|it|
let color = match $it.strategy.type {
"single" => { $it.strategy.n | get-color },
"double" => {
let c1 = $it.strategy.n | get-color | color from-string $in
let c2 = $it.strategy.m | get-color | color from-string $in
let c = $it.strategy.p
color mix $c1 $c2 $c | color to-hex
},
}
let alpha = match $it.strategy.type {
"single" => 1.0,
"double" => 0.3,
}
let type = match $it.strategy.type {
"single" => "solid",
"double" => "dashed",
}
{ color: $color, line: { alpha: $alpha, type: $type } }
}
| reject strategy
| save --force /tmp/graphs.json
let x_min = open /tmp/graphs.json | get points | flatten | get x | math min
let x_max = open /tmp/graphs.json | get points | flatten | get x | math max
let args = [
--json-data-file /tmp/graphs.json
--x-lim ($x_min - 1) ($x_max + 1)
--y-lim -0.01 1.01
--fullscreen
# --title "diversity over time when recoding shards $r$ shards"
--x-label "time (in nb of steps)"
--y-label "diversity $\\delta$"
--dpi 150
--fig-size ...[16, 5]
--font ({ size: 15, family: serif, sans-serif: Helvetica } | to json)
--use-tex
--legend-loc "upper right"
(if $save != null { [ --save $save ] })
]
gplt plot ...($args | flatten | compact)
}
use consts.nu
use $consts.NUSHELL error "error throw"
const VALID_HEX_CHARS = "abcdefABCDEF0123456789"
def check-hex [-n: int]: [
string -> record<
ok: bool,
err: record<msg: string, label: string, help: string>,
>
] {
let s = $in
if ($s | str length) != $n {
return {
ok: false,
err: {
msg: "invalid HEX length"
label : $"length is ($s | str length)",
help: "length should be 64",
},
}
}
for c in ($s | split chars | enumerate) {
if not ($VALID_HEX_CHARS | str contains $c.item) {
return {
ok: false,
err: {
msg: "bad HEX character",
label: $"found '($c.item)' at ($c.index)",
help: $"expected one of '($VALID_HEX_CHARS)'",
},
}
}
}
{ ok: true, err: {} }
}
export def main [
--options: record<
nb_bytes: int,
k: int,
n: int,
nb_measurements: int,
nb_scenarii: int,
measurement_schedule: int,
measurement_schedule_start: int,
max_t: int,
strategies: list<string>,
environment: string,
>,
--prng-seed: string = "0000000000000000000000000000000000000000000000000000000000000000",
] {
if $options.measurement_schedule_start > $options.max_t {
error make --unspanned {
msg: $"measurement schedule will start after the max t, ($options.measurement_schedule_start) > ($options.max_t)"
}
}
let res = $prng_seed | check-hex -n 64
if not $res.ok {
error throw {
err: $res.err.msg,
label: $res.err.label,
span: (metadata $prng_seed).span,
help: $res.err.help,
}
}
let exp_hash = $options | select nb_bytes k n environment | sort | to nuon | hash sha256
for s in $options.strategies {
let output_dir = [
$consts.CACHE,
$"($prng_seed)",
([$options.environment, $s, $options.k, $options.n, $options.nb_bytes] | str join '-')
] | path join
mkdir $output_dir
print $"data will be dumped to `($output_dir)`"
for i in 1..$options.nb_scenarii {
let seed = [ $prng_seed, $exp_hash, $s, $i ] | str join | hash sha256
let output = [ $output_dir, $seed ] | path join
^$consts.BIN ...[
$options.nb_bytes,
-k $options.k
-n $options.n
--nb-measurements $options.nb_measurements
--measurement-schedule $options.measurement_schedule
--measurement-schedule-start $options.measurement_schedule_start
-t $options.max_t
--strategy $s
--environment $options.environment
--prng-seed $seed
] out> $output
}
print $"data has been dumped to `($output_dir)`"
}
}
use consts.nu CACHE
use path.nu [ "remove-cache-prefix" ]
def pretty-hash []: [ string -> string ] {
str substring 0..7
}
export def main [] {
watch $CACHE { |op, path, new_path|
if $op != "Create" {
return
}
let path = $path | remove-cache-prefix
let now = date now | format date "%Y-%m-%d %H:%M:%S"
let p = $path | parse "{seed}/{exp}/{id}" | into record
if $p == {} {
let p = $path | parse "{seed}/{exp}" | into record
if $p == {} {
return $path
}
return $"($p.seed | pretty-hash) ($p.exp) at ($now)"
}
$"($p.seed | pretty-hash) ($p.exp) ($p.id | pretty-hash) at ($now)"
}
}
use rand::{Rng, RngCore};
use crate::random::draw_unique_elements;
#[derive(Debug, PartialEq)]
pub(super) enum Environment {
/// at each time step, remove `n` elements with probability `p`
RandomFixed { p: f64, n: usize },
/// at each time step, remove a fraction `q` of the elements with probability `p`
RandomDynamic { p: f64, q: f64 },
/// at each time step, remove `n` elements
Fixed { n: usize },
}
impl Environment {
/// `update(things, rng)` is `things` with some elements potentially removed according to the
/// [`Environment`] type
pub(super) fn update<T: Clone>(&self, things: &[T], rng: &mut impl RngCore) -> Vec<T> {
let nb_to_take = match self {
Environment::Fixed { n } => things.len() - n,
Environment::RandomFixed { p, n } => {
if rng.gen::<f64>() > *p {
return things.to_vec();
}
things.len() - n
}
Environment::RandomDynamic { p, q } => {
if rng.gen::<f64>() > *p {
return things.to_vec();
}
(things.len() as f64 * (1.0 - q)) as usize
}
};
draw_unique_elements(things, nb_to_take, rng)
}
pub(super) fn from_str(s: &str) -> Result<Self, String> {
let tokens: Vec<&str> = s.split(':').collect();
if tokens.is_empty() {
return Err(format!(
"expected at least one :-separated token in '{}', found 0",
s,
));
}
match tokens[0] {
"fixed" => {
let n = match tokens[1].parse::<usize>() {
Ok(u) => u,
Err(_) => {
return Err(format!(
"could not parse positive integer from '{}'",
tokens[1]
))
}
};
Ok(Environment::Fixed { n })
}
"random-fixed" => {
if tokens.len() != 3 {
return Err(format!(
"expected 3 :-separated tokens in '{}', found {}",
s,
tokens.len(),
));
}
let p = match tokens[1].parse::<f64>() {
Ok(f) => f,
Err(_) => return Err(format!("could not parse float from '{}'", tokens[1])),
};
if !(0.0..=1.0).contains(&p) {
return Err(format!("p should be a probability, found {}", p));
}
let n = match tokens[2].parse::<usize>() {
Ok(u) => u,
Err(_) => {
return Err(format!(
"could not parse positive integer from '{}'",
tokens[2]
))
}
};
Ok(Environment::RandomFixed { p, n })
}
"random-dynamic" => {
if tokens.len() != 3 {
return Err(format!(
"expected 3 :-separated tokens in '{}', found {}",
s,
tokens.len(),
));
}
let p = match tokens[1].parse::<f64>() {
Ok(f) => f,
Err(_) => return Err(format!("could not parse float from '{}'", tokens[1])),
};
if !(0.0..=1.0).contains(&p) {
return Err(format!("p should be a probability, found {}", p));
}
let q = match tokens[2].parse::<f64>() {
Ok(f) => f,
Err(_) => return Err(format!("could not parse float from '{}'", tokens[2])),
};
if !(0.0..=1.0).contains(&q) {
return Err(format!("q should be between 0 and 1, found {}", q));
}
Ok(Environment::RandomDynamic { p, q })
}
ty => Err(format!("unknow env type '{}'", ty)),
}
}
}
#[cfg(test)]
mod test {
#[test]
fn environment() {
assert_eq!(
super::Environment::from_str("fixed:1"),
Ok(super::Environment::Fixed { n: 1 })
);
assert_eq!(
super::Environment::from_str("random-fixed:0.2:1"),
Ok(super::Environment::RandomFixed { p: 0.2, n: 1 })
);
assert_eq!(
super::Environment::from_str("random-dynamic:0.2:0.3"),
Ok(super::Environment::RandomDynamic { p: 0.2, q: 0.3 })
);
let cases = vec![
("foo", "unknow env type 'foo'"),
("foo:", "unknow env type 'foo'"),
("fixed:", "could not parse positive integer from ''"),
("fixed:foo", "could not parse positive integer from 'foo'"),
(
"random-fixed:",
"expected 3 :-separated tokens in 'random-fixed:', found 2",
),
("random-fixed:foo:", "could not parse float from 'foo'"),
("random-fixed:1.2:", "p should be a probability, found 1.2"),
(
"random-fixed:0.2:",
"could not parse positive integer from ''",
),
(
"random-fixed:0.2:foo",
"could not parse positive integer from 'foo'",
),
(
"random-dynamic:",
"expected 3 :-separated tokens in 'random-dynamic:', found 2",
),
("random-dynamic:foo:", "could not parse float from 'foo'"),
(
"random-dynamic:1.2:",
"p should be a probability, found 1.2",
),
("random-dynamic:0.2:", "could not parse float from ''"),
("random-dynamic:0.2:foo", "could not parse float from 'foo'"),
(
"random-dynamic:0.2:1.2",
"q should be between 0 and 1, found 1.2",
),
];
for (input, expected_error) in cases {
assert_eq!(
super::Environment::from_str(input),
Err(expected_error.to_string()),
"input: {}",
input
);
}
}
}
use std::process::exit;
use ark_ff::PrimeField;
use clap::Parser;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use komodo::{
error::KomodoError,
fec::{self, Shard},
linalg::Matrix,
};
use rand::{rngs::StdRng, Rng, RngCore, SeedableRng};
use random::draw_unique_elements;
mod environment;
mod random;
mod strategy;
use crate::{environment::Environment, strategy::Strategy};
fn random_bytes(n: usize, rng: &mut impl RngCore) -> Vec<u8> {
(0..n).map(|_| rng.gen::<u8>()).collect()
}
fn setup<F: PrimeField>(bytes: &[u8], k: usize, n: usize) -> Result<Vec<Shard<F>>, KomodoError> {
let points: Vec<F> = (0..n)
.map(|i| F::from_le_bytes_mod_order(&i.to_le_bytes()))
.collect();
let encoding_mat = Matrix::vandermonde_unchecked(&points, k);
let shards = fec::encode(bytes, &encoding_mat)?;
Ok(shards)
}
fn measure_inbreeding<F: PrimeField>(
shards: &[Shard<F>],
k: usize,
nb_measurements: usize,
mp: &MultiProgress,
sty: &ProgressStyle,
rng: &(impl RngCore + Clone),
) -> f64 {
let mut rng = rng.clone();
let mut count = 0;
let pb = mp.add(ProgressBar::new(nb_measurements as u64));
pb.set_style(sty.clone());
pb.set_message("measure");
for _ in 0..nb_measurements {
if fec::decode(draw_unique_elements(shards, k, &mut rng)).is_ok() {
count += 1;
}
pb.inc(1);
}
count as f64 / nb_measurements as f64
}
#[allow(clippy::too_many_arguments)]
fn run<F, Fun>(
bytes: &[u8],
k: usize,
n: usize,
max_t: usize,
strategy: Strategy,
env: Environment,
nb_measurements: usize,
measurement_schedule: Fun,
rng: &mut (impl RngCore + Clone),
) -> Result<(), KomodoError>
where
F: PrimeField,
Fun: Fn(usize) -> bool,
{
let mut shards = setup::<F>(bytes, k, n)?;
let mp = MultiProgress::new();
let sty = ProgressStyle::with_template("{msg}: {bar:40.cyan/blue} {pos:>7}/{len:7}")
.unwrap()
.progress_chars("##-");
let pb = mp.add(ProgressBar::new(max_t as u64));
pb.set_style(sty.clone());
pb.set_message("main");
for t in 0..=max_t {
if shards.len() < k {
break;
}
if measurement_schedule(t) {
let inbreeding = measure_inbreeding(&shards, k, nb_measurements, &mp, &sty, rng);
println!("{}, {}", t, inbreeding);
}
// recode a new random shard
let new_shard = fec::recode_random(&strategy.draw(&shards, rng), rng)
.unwrap()
.unwrap();
shards.push(new_shard);
shards = env.update(&shards, rng);
pb.inc(1);
}
pb.finish_with_message("done");
Ok(())
}
fn parse_hex_string(s: &str) -> Result<[u8; 32], String> {
if s.len() != 64 {
return Err("Input string must be exactly 64 characters long".to_string());
}
match hex::decode(s) {
// `bytes` will be a `Vec<u8>` of size `32`, so it's safe to `unwrap`
// the conversion to `[u8: 32]`
Ok(bytes) => Ok(bytes.try_into().unwrap()),
Err(e) => Err(format!("Failed to decode hex string: {}", e)),
}
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[arg()]
nb_bytes: usize,
#[arg(short)]
k: usize,
#[arg(short)]
n: usize,
#[arg(short)]
t: usize,
/// something of the form `<p>:<i>,<j>`
/// at each time step, shard $i$ will be used for recoding with probability $p$, otherwise, $j$
/// will be used with probability $1 - p$
#[arg(long)]
strategy: String,
/// something of the form `random-dynamic:<p>:<q>` where a proportion $q$ of the shards will be removed at
/// each step with probability $p$
#[arg(long)]
environment: String,
/// the number of measurements to repeat each case, larger values will reduce the variance of
/// the measurements
#[arg(long)]
nb_measurements: usize,
#[arg(long)]
measurement_schedule: usize,
#[arg(long)]
measurement_schedule_start: usize,
#[arg(long, value_parser = parse_hex_string)]
prng_seed: [u8; 32],
}
fn main() {
let cli = Cli::parse();
if cli.nb_measurements == 0 {
eprintln!(
"`--nb-measurements` should be a strictly positive integer, found {}",
cli.nb_measurements
);
exit(1);
}
let mut rng = StdRng::from_seed(cli.prng_seed);
let bytes = random_bytes(cli.nb_bytes, &mut rng);
eprintln!(
"diversity will be measured every {} steps",
cli.measurement_schedule
);
let measurement_schedule = |t| {
t >= cli.measurement_schedule_start
&& (t - cli.measurement_schedule_start) % cli.measurement_schedule == 0
};
let environment = Environment::from_str(&cli.environment).unwrap();
let strategy = Strategy::from_str(&cli.strategy).unwrap();
eprintln!(
"k-recoding: k = {}, n = {}, strategy = {:?}, environment = {:?}",
cli.k, cli.n, strategy, environment,
);
let _ = run::<ark_pallas::Fr, _>(
&bytes,
cli.k,
cli.n,
cli.t,
strategy,
environment,
cli.nb_measurements,
measurement_schedule,
&mut rng,
);
}
use rand::{seq::SliceRandom, Rng, RngCore};
use std::collections::HashSet;
#[allow(dead_code)]
fn draw_unique_indices(n: usize, vec_len: usize, rng: &mut impl RngCore) -> HashSet<usize> {
let mut indices = HashSet::new();
while indices.len() < n {
let idx = rng.gen_range(0..vec_len);
indices.insert(idx);
}
indices
}
pub(super) fn draw_unique_elements<T: Clone>(
things: &[T],
n: usize,
rng: &mut impl RngCore,
) -> Vec<T> {
let mut things = things.to_vec();
things.shuffle(rng);
things.iter().take(n).cloned().collect()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment