diff --git a/Cargo.toml b/Cargo.toml index 404f080bf421ce0213e693d17cf42e3b8280519d..103b5b7c06fb5c85961643b82d36a5aa6a4e757e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ rand = "0.8.5" [workspace] members = [ "benchmarks", - "bins/inbreeding", "bins/rank", ] diff --git a/bins/inbreeding/Cargo.toml b/bins/inbreeding/Cargo.toml deleted file mode 100644 index 5fbcc6973bda64d8ceafa4d6d7aa506a46208afc..0000000000000000000000000000000000000000 --- a/bins/inbreeding/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[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" diff --git a/bins/inbreeding/README.md b/bins/inbreeding/README.md deleted file mode 100644 index cb18b6b1b09f537741247fd1b46b7b6592197adb..0000000000000000000000000000000000000000 --- a/bins/inbreeding/README.md +++ /dev/null @@ -1,7 +0,0 @@ -- 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) diff --git a/bins/inbreeding/mod.nu b/bins/inbreeding/mod.nu deleted file mode 100644 index bb0b45a4ea81ee30605a4db3c30e6978d6d3bb52..0000000000000000000000000000000000000000 --- a/bins/inbreeding/mod.nu +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/bins/inbreeding/scripts/plot.nu b/bins/inbreeding/scripts/plot.nu deleted file mode 100644 index 335746c281ba4c60243a13134b6faed7d6d8c5c9..0000000000000000000000000000000000000000 --- a/bins/inbreeding/scripts/plot.nu +++ /dev/null @@ -1,11 +0,0 @@ -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 -} diff --git a/bins/inbreeding/scripts/run.nu b/bins/inbreeding/scripts/run.nu deleted file mode 100644 index aac3c5865b79c015aa43fc30b100b7e81d2a10e8..0000000000000000000000000000000000000000 --- a/bins/inbreeding/scripts/run.nu +++ /dev/null @@ -1,57 +0,0 @@ -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 - } -} diff --git a/bins/inbreeding/scripts/show.nu b/bins/inbreeding/scripts/show.nu deleted file mode 100644 index 15e22d991131f575ed05611f6a5651a9d2ae9f9b..0000000000000000000000000000000000000000 --- a/bins/inbreeding/scripts/show.nu +++ /dev/null @@ -1,55 +0,0 @@ -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-*" diff --git a/bins/inbreeding/src/.nushell/build.nu b/bins/inbreeding/src/.nushell/build.nu deleted file mode 100644 index 75de47de734055fae94aeaa185e9861cdcddcde6..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/build.nu +++ /dev/null @@ -1,4 +0,0 @@ -export def main [] { - cd bins/inbreeding - cargo build --release -} diff --git a/bins/inbreeding/src/.nushell/consts.nu b/bins/inbreeding/src/.nushell/consts.nu deleted file mode 100644 index f7357d9df011374c6a7ae6a6acc593dd64c3c592..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/consts.nu +++ /dev/null @@ -1,4 +0,0 @@ -export const BIN = "./target/release/inbreeding" -export const CACHE = ($nu.home-path | path join .cache komodo inbreeding) - -export const NUSHELL = "../../../../.nushell" diff --git a/bins/inbreeding/src/.nushell/inspect.nu b/bins/inbreeding/src/.nushell/inspect.nu deleted file mode 100644 index cb02c629618ff77d696dd724f456aa9572350f0c..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/inspect.nu +++ /dev/null @@ -1,32 +0,0 @@ -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 -} - diff --git a/bins/inbreeding/src/.nushell/list.nu b/bins/inbreeding/src/.nushell/list.nu deleted file mode 100644 index 38a386ba51a9ea09d012e8f182a01fb78e85abc6..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/list.nu +++ /dev/null @@ -1,16 +0,0 @@ -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 -} diff --git a/bins/inbreeding/src/.nushell/load.nu b/bins/inbreeding/src/.nushell/load.nu deleted file mode 100644 index d410a90ab6eb787a4ccfd7d96a2982920b01e533..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/load.nu +++ /dev/null @@ -1,71 +0,0 @@ -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, - } -} diff --git a/bins/inbreeding/src/.nushell/parse.nu b/bins/inbreeding/src/.nushell/parse.nu deleted file mode 100644 index 6f251e74af4d4f1214d5454b2526f94f80be7496..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/parse.nu +++ /dev/null @@ -1,52 +0,0 @@ -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 -} diff --git a/bins/inbreeding/src/.nushell/path.nu b/bins/inbreeding/src/.nushell/path.nu deleted file mode 100644 index 4614db12ff3e211215ad1d5cebc21ba0a4ac2ae4..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/path.nu +++ /dev/null @@ -1,5 +0,0 @@ -use consts.nu - -export def remove-cache-prefix []: path -> string { - str replace $"($consts.CACHE)(char path_sep)" '' -} diff --git a/bins/inbreeding/src/.nushell/plot.nu b/bins/inbreeding/src/.nushell/plot.nu deleted file mode 100644 index 4405a12b22f49e649593777a953c4e4761ca4575..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/plot.nu +++ /dev/null @@ -1,124 +0,0 @@ -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) - -} diff --git a/bins/inbreeding/src/.nushell/run.nu b/bins/inbreeding/src/.nushell/run.nu deleted file mode 100644 index 54e5c9335ad52169ff702c0f8bb2a67223a05393..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/run.nu +++ /dev/null @@ -1,103 +0,0 @@ -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)`" - } -} diff --git a/bins/inbreeding/src/.nushell/watch.nu b/bins/inbreeding/src/.nushell/watch.nu deleted file mode 100644 index 6115192b75f2f3a31588c180639f7cf39ab214f3..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/.nushell/watch.nu +++ /dev/null @@ -1,30 +0,0 @@ -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)" - } -} diff --git a/bins/inbreeding/src/environment.rs b/bins/inbreeding/src/environment.rs deleted file mode 100644 index 11357823e32f5f68886ac738c1efdfa79a175368..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/environment.rs +++ /dev/null @@ -1,181 +0,0 @@ -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 - ); - } - } -} diff --git a/bins/inbreeding/src/main.rs b/bins/inbreeding/src/main.rs deleted file mode 100644 index 9ab72c6ea09468c276c53e5360153ae1ebae7b77..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/main.rs +++ /dev/null @@ -1,201 +0,0 @@ -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, - ); -} diff --git a/bins/inbreeding/src/random.rs b/bins/inbreeding/src/random.rs deleted file mode 100644 index d5cb9276b7efd99979a95e20d11e5a4c5cab6a7c..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/random.rs +++ /dev/null @@ -1,25 +0,0 @@ -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() -} diff --git a/bins/inbreeding/src/strategy.rs b/bins/inbreeding/src/strategy.rs deleted file mode 100644 index ffe573606c721a452802bd4f95ec1d0905255593..0000000000000000000000000000000000000000 --- a/bins/inbreeding/src/strategy.rs +++ /dev/null @@ -1,148 +0,0 @@ -use rand::{Rng, RngCore}; - -use crate::random::draw_unique_elements; - -#[derive(Debug, PartialEq)] -pub(super) enum Strategy { - Single { n: usize }, - Double { p: f64, n: usize, m: usize }, -} - -impl Strategy { - pub(super) fn draw<T: Clone>(&self, things: &[T], rng: &mut impl RngCore) -> Vec<T> { - let nb_to_take = match self { - Self::Single { n } => *n, - Self::Double { p, n, m } => { - if rng.gen::<f64>() < *p { - *n - } else { - *m - } - } - }; - - draw_unique_elements(things, nb_to_take, rng) - } - - pub(super) fn from_str(s: &str) -> Result<Self, String> { - if !s.contains(':') { - return Err(format!( - "expected at least one :-separated token in '{}', found 0", - s, - )); - } - - let tokens: Vec<&str> = s.split(':').collect(); - - match tokens[0] { - "single" => { - if tokens.len() != 2 { - return Err(format!( - "expected 2 :-separated tokens in '{}', found {}", - s, - tokens.len(), - )); - } - - let n = match tokens[1].parse::<usize>() { - Ok(u) => u, - Err(_) => { - return Err(format!( - "could not parse positive integer from '{}'", - tokens[1] - )) - } - }; - Ok(Self::Single { n }) - } - "double" => { - if tokens.len() != 4 { - return Err(format!( - "expected 4 :-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] - )) - } - }; - let m = match tokens[3].parse::<usize>() { - Ok(u) => u, - Err(_) => { - return Err(format!( - "could not parse positive integer from '{}'", - tokens[3] - )) - } - }; - - Ok(Self::Double { p, n, m }) - } - ty => Err(format!("unknown strat type '{}'", ty)), - } - } -} - -#[cfg(test)] -mod test { - #[test] - fn strategy() { - assert_eq!( - super::Strategy::from_str("single:3"), - Ok(super::Strategy::Single { n: 3 }) - ); - assert_eq!( - super::Strategy::from_str("double:0.1:1:2"), - Ok(super::Strategy::Double { p: 0.1, n: 1, m: 2 }) - ); - - let cases = vec![ - ( - "foo", - "expected at least one :-separated token in 'foo', found 0", - ), - ("foo:bar:baz", "unknown strat type 'foo'"), - ("single:", "could not parse positive integer from ''"), - ( - "single::", - "expected 2 :-separated tokens in 'single::', found 3", - ), - ( - "double:bar:baz", - "expected 4 :-separated tokens in 'double:bar:baz', found 3", - ), - ( - "double:bar:baz:spam:eggs", - "expected 4 :-separated tokens in 'double:bar:baz:spam:eggs', found 5", - ), - ("double:bar::", "could not parse float from 'bar'"), - ("double:1.2::", "p should be a probability, found 1.2"), - ("double:0.2::", "could not parse positive integer from ''"), - ("double:0.2:1:", "could not parse positive integer from ''"), - ]; - - for (input, expected_error) in cases { - assert_eq!( - super::Strategy::from_str(input), - Err(expected_error.to_string()), - "input: {}", - input, - ); - } - } -}