Skip to content

Commit

Permalink
Merge pull request #1 from noahbaculi/memoization
Browse files Browse the repository at this point in the history
Memoization
  • Loading branch information
noahbaculi authored Jul 28, 2023
2 parents 2344481 + aee0d75 commit 9fa9d8c
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 187 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"liveServer.settings.port": 5501
}
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ anyhow = "1.0.71"
average = { version = "0.14.1", features = ["rayon"] }
itertools = "0.11.0"
js-sys = "0.3.64"
memoize = "0.4.0"
ordered-float = "3.7.0"
pathfinding = "4.3.0"
regex = "1.9.1"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ flowchart TB
Beat1 ~~~ Beat2 ~~~ Beat3 ~~~ Beat4
```

With the node links, we can see the directed graph take shape:
With the node edges, we can see the directed graph take shape:

```mermaid
%%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
Expand Down Expand Up @@ -247,8 +247,8 @@ flowchart TB

The number of fingering combinations grows exponentially with more beats and pitches so the choice of [shortest path algorithm](https://en.wikipedia.org/wiki/Shortest_path_problem) is critical. The [Dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) pathfinding algorithm was chosen for this application of the "shortest path exercise" for the following reasons:

- The sequential nature of the musical arrangement problem results in a _directed_ graph where only nodes representing consecutive beat fingering combinations are linked.
- The links between nodes are _weighted_ with the difficulty of moving from one fingering combination to another graph above is already constructed with the only possible next nodes connected.
- The sequential nature of the musical arrangement problem results in a _directed_ graph where only nodes representing consecutive beat fingering combinations have edges.
- The edges between nodes are _weighted_ with the difficulty of moving from one fingering combination to another graph above is already constructed with the only possible next nodes connected.

## Contributing and Installation

Expand Down
258 changes: 129 additions & 129 deletions benches/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use anyhow::{anyhow, Result};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use guitar_tab_generator::{
arrangement::{create_arrangements, BeatVec, Line},
arrangement::{self, create_arrangements, BeatVec, Line},
guitar::{create_string_tuning, Guitar, STD_6_STRING_TUNING_OPEN_PITCHES},
parser::parse_lines,
pitch::Pitch,
Expand All @@ -13,56 +13,6 @@ use guitar_tab_generator::{
use itertools::Itertools;
use std::{collections::BTreeMap, time::Duration};

pub fn guitar_creation(c: &mut Criterion) {
let six_string_tuning = create_string_tuning(&STD_6_STRING_TUNING_OPEN_PITCHES);

let three_string_tuning = create_string_tuning(&[Pitch::E4, Pitch::B3, Pitch::G3]);
let twelve_string_tuning = create_string_tuning(&[
Pitch::E4,
Pitch::B3,
Pitch::G3,
Pitch::D3,
Pitch::A2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
]);

const STANDARD_NUM_FRETS: u8 = 18;

c.bench_function("create_standard_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(six_string_tuning.clone()),
black_box(STANDARD_NUM_FRETS),
black_box(0),
)
})
});
c.bench_function("create_few_fret_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(six_string_tuning.clone()),
black_box(3),
black_box(0),
)
})
});
c.bench_function("create_few_string_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(three_string_tuning.clone()),
black_box(STANDARD_NUM_FRETS),
black_box(0),
)
})
});
}

fn fur_elise_input() -> &'static str {
"E4
Eb4
Expand Down Expand Up @@ -154,37 +104,106 @@ fn fur_elise_input() -> &'static str {
B3
C4"
}
fn fur_elise_lines() -> Vec<Line<BeatVec<Pitch>>> {
guitar_tab_generator::parser::parse_lines(fur_elise_input().to_owned()).unwrap()
}

fn fur_elise_lines() -> Result<Vec<Line<BeatVec<Pitch>>>> {
guitar_tab_generator::parser::parse_lines(fur_elise_input().to_owned())
fn bench_parse_lines(c: &mut Criterion) {
let fur_elise_input = fur_elise_input();

c.bench_function("parse_lines", |b| {
b.iter(|| {
guitar_tab_generator::parser::memoized_original_parse_lines(fur_elise_input.to_owned())
})
});
}

fn bench_create_string_tuning_offset(c: &mut Criterion) {
c.bench_function("create_string_tuning_offset", |b| {
b.iter(|| {
guitar_tab_generator::parser::create_string_tuning_offset(
guitar_tab_generator::parser::parse_tuning(black_box("random")),
)
})
});
}

fn guitar_creation(c: &mut Criterion) {
let six_string_tuning = create_string_tuning(&STD_6_STRING_TUNING_OPEN_PITCHES);

let three_string_tuning = create_string_tuning(&[Pitch::E4, Pitch::B3, Pitch::G3]);
let twelve_string_tuning = create_string_tuning(&[
Pitch::E4,
Pitch::B3,
Pitch::G3,
Pitch::D3,
Pitch::A2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
Pitch::E2,
]);

const STANDARD_NUM_FRETS: u8 = 18;

c.bench_function("create_standard_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(six_string_tuning.clone()),
black_box(STANDARD_NUM_FRETS),
black_box(0),
)
})
});
c.bench_function("create_few_fret_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(six_string_tuning.clone()),
black_box(3),
black_box(0),
)
})
});
c.bench_function("create_few_string_guitar", |b| {
b.iter(|| {
Guitar::new(
black_box(three_string_tuning.clone()),
black_box(STANDARD_NUM_FRETS),
black_box(0),
)
})
});
}

fn bench_arrangement_creation(c: &mut Criterion) {
let tuning = create_string_tuning(&STD_6_STRING_TUNING_OPEN_PITCHES);

// c.bench_function("fur_elise_1_arrangement", |b| {
// b.iter(|| {
// create_arrangements(
// black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
// black_box(fur_elise_lines().unwrap()),
// black_box(1),
// )
// })
// });
// c.bench_function("fur_elise_3_arrangements", |b| {
// b.iter(|| {
// create_arrangements(
// black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
// black_box(fur_elise_lines().unwrap()),
// black_box(3),
// )
// })
// });
c.bench_function("fur_elise_1_arrangement", |b| {
b.iter(|| {
arrangement::memoized_original_create_arrangements(
black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
black_box(fur_elise_lines()),
black_box(1),
)
})
});
c.bench_function("fur_elise_3_arrangements", |b| {
b.iter(|| {
arrangement::memoized_original_create_arrangements(
black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
black_box(fur_elise_lines()),
black_box(3),
)
})
});
c.bench_function("fur_elise_5_arrangements", |b| {
b.iter(|| {
create_arrangements(
arrangement::memoized_original_create_arrangements(
black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
black_box(fur_elise_lines().unwrap()),
black_box(fur_elise_lines()),
black_box(5),
)
})
Expand All @@ -196,15 +215,14 @@ fn bench_arrangement_scaling(c: &mut Criterion) {

let mut group = c.benchmark_group("bench_arrangement_scaling");
for num in (0..=22) {
// group.throughput(Throughput::Bytes(*size as u64));
group
.sample_size(15)
.warm_up_time(Duration::from_secs_f32(2.0));
group.bench_with_input(BenchmarkId::from_parameter(num), &num, |b, &num| {
b.iter(|| {
create_arrangements(
arrangement::memoized_original_create_arrangements(
black_box(Guitar::new(tuning.clone(), 18, 0).unwrap()),
black_box(fur_elise_lines().unwrap()),
black_box(fur_elise_lines()),
black_box(num),
)
});
Expand All @@ -213,22 +231,37 @@ fn bench_arrangement_scaling(c: &mut Criterion) {
group.finish();
}

fn bench_parse_lines(c: &mut Criterion) {
let fur_elise_input = fur_elise_input();
fn bench_render_tab(c: &mut Criterion) {
let mut group = c.benchmark_group("render_tab");

c.bench_function("parse_lines", |b| {
b.iter(|| guitar_tab_generator::parser::parse_lines(fur_elise_input.to_owned()))
});
}
let arrangements = create_arrangements(
Guitar::default(),
parse_lines(fur_elise_input().to_owned()).unwrap(),
1,
)
.unwrap();

fn bench_create_string_tuning_offset(c: &mut Criterion) {
c.bench_function("create_string_tuning_offset", |b| {
b.iter(|| {
guitar_tab_generator::parser::create_string_tuning_offset(
guitar_tab_generator::parser::parse_tuning(black_box("random")),
)
})
});
for playback_index in (0..=30).step_by(10) {
// group
// .sample_size(20)
// .warm_up_time(Duration::from_secs_f32(3.0));
group.bench_with_input(
BenchmarkId::from_parameter(playback_index),
&playback_index,
|b, &playback_index| {
b.iter(|| {
render_tab(
black_box(&arrangements[0].lines),
black_box(&Guitar::default()),
black_box(20),
black_box(2),
black_box(Some(playback_index)),
);
});
},
);
}
group.finish();
}

fn bench_create_single_composition_scaling(c: &mut Criterion) {
Expand Down Expand Up @@ -291,50 +324,17 @@ fn bench_create_single_composition_large_scaling(c: &mut Criterion) {
group.finish();
}

fn bench_render_tab(c: &mut Criterion) {
let mut group = c.benchmark_group("render_tab");

let arrangements = create_arrangements(
Guitar::default(),
parse_lines(fur_elise_input().to_owned()).unwrap(),
1,
)
.unwrap();

for playback_index in (0..=30).step_by(10) {
// group
// .sample_size(20)
// .warm_up_time(Duration::from_secs_f32(3.0));
group.bench_with_input(
BenchmarkId::from_parameter(playback_index),
&playback_index,
|b, &playback_index| {
b.iter(|| {
render_tab(
black_box(&arrangements[0].lines),
black_box(&Guitar::default()),
black_box(20),
black_box(2),
black_box(Some(playback_index)),
);
});
},
);
}
group.finish();
}

criterion_group! {
name=benches;
config = Criterion::default().noise_threshold(0.05).sample_size(15);
targets =
bench_parse_lines,
bench_create_string_tuning_offset,
guitar_creation,
// arrangement_creation,
// bench_arrangement_scaling,
// parse_lines,
// create_string_tuning_offset,
// bench_create_single_composition_scaling,
bench_arrangement_creation,
bench_arrangement_scaling,
bench_create_single_composition_scaling,
bench_create_single_composition_large_scaling,
// bench_render_tab
bench_render_tab
}
criterion_main!(benches);
11 changes: 9 additions & 2 deletions examples/advanced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ fn main() -> Result<()> {
A3"
.to_string();

let lines: Vec<Line<Vec<Pitch>>> = parse_lines(input)?;
let lines: Vec<Line<Vec<Pitch>>> = match parse_lines(input) {
Ok(input_lines) => input_lines,
Err(e) => return Err(std::sync::Arc::try_unwrap(e).unwrap()),
};

let tuning = create_string_tuning(&[
Pitch::E4,
Expand All @@ -46,7 +49,11 @@ fn main() -> Result<()> {
// dbg!(&guitar);

let num_arrangements = 1;
let arrangements = create_arrangements(guitar.clone(), lines, num_arrangements)?;
let arrangements = match create_arrangements(guitar.clone(), lines, num_arrangements) {
Ok(arrangements) => arrangements,
Err(e) => return Err(std::sync::Arc::try_unwrap(e).unwrap()),
};

// dbg!(&arrangements);

let tab_width = 20;
Expand Down
2 changes: 1 addition & 1 deletion examples/wasm.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
let endTime = performance.now();
let duration = (endTime - startTime).toFixed(1);
console.log(`Arrangement generated in ${duration} milliseconds:`, compositions[0]);
console.log(`Tab:\n`, compositions[0].tab);
console.log(`Tab:\n${compositions[0].tab}`);

console.log(get_tuning_names());
});
Expand Down
Loading

0 comments on commit 9fa9d8c

Please sign in to comment.