diff --git a/lang-trials/rust-benchmark-native/.gitignore b/lang-trials/rust-benchmark-native/.gitignore new file mode 100644 index 0000000..5b910ca --- /dev/null +++ b/lang-trials/rust-benchmark-native/.gitignore @@ -0,0 +1,3 @@ +target/* +dist/* +Cargo.lock \ No newline at end of file diff --git a/lang-trials/rust-benchmark-native/Cargo.toml b/lang-trials/rust-benchmark-native/Cargo.toml new file mode 100644 index 0000000..0ed8747 --- /dev/null +++ b/lang-trials/rust-benchmark-native/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rust-benchmark-native" +version = "0.1.0" +authors = ["Aaron"] +edition = "2021" + +[dependencies] +cairo-rs = "0.20.1" +gtk = { package = "gtk4", version = "0.9.0" } +##minifb = "0.27.0" +nalgebra = "0.33.0" +plotters = "0.3.6" +plotters-cairo = "0.7.0" +##plotters-gtk4 = { git = "https://github.com/baarkerlounger/plotters-gtk4.git", branch = "patch-1" } + +[profile.release] +opt-level = "s" # optimize for small code size +debug = true # include debug symbols diff --git a/lang-trials/rust-benchmark-native/src/engine.rs b/lang-trials/rust-benchmark-native/src/engine.rs new file mode 100644 index 0000000..eed73a1 --- /dev/null +++ b/lang-trials/rust-benchmark-native/src/engine.rs @@ -0,0 +1,105 @@ +use nalgebra::{*, allocator::Allocator}; +use std::f64::consts::{PI, E}; + +/* dynamic matrices */ +pub fn rand_eigval_series(dim: usize, time_res: usize) -> Vec, Dyn>> { + // initialize the random matrix + let mut rand_mat = DMatrix::::from_fn(dim, dim, |j, k| { + let n = j*dim + k; + E*((n*n) as f64) % 2.0 - 1.0 + }) * (3.0 / (dim as f64)).sqrt(); + + // initialize the rotation step + let mut rot_step = DMatrix::::identity(dim, dim); + let max_freq = 4; + for n in (0..dim).step_by(2) { + let ang = PI * ((n % max_freq) as f64) / (time_res as f64); + let ang_cos = ang.cos(); + let ang_sin = ang.sin(); + rot_step[(n, n)] = ang_cos; + rot_step[(n+1, n)] = ang_sin; + rot_step[(n, n+1)] = -ang_sin; + rot_step[(n+1, n+1)] = ang_cos; + } + + // find the eigenvalues + let mut eigval_series = Vec::, Dyn>>::with_capacity(time_res); + eigval_series.push(rand_mat.complex_eigenvalues()); + for _ in 1..time_res { + rand_mat = &rot_step * rand_mat; + eigval_series.push(rand_mat.complex_eigenvalues()); + } + eigval_series +} + +/* dynamic single float matrices */ +/*pub fn rand_eigval_series(dim: usize, time_res: usize) -> Vec, Dyn>> { + // initialize the random matrix + let mut rand_mat = DMatrix::::from_fn(dim, dim, |j, k| { + let n = j*dim + k; + (E as f32)*((n*n) as f32) % 2.0_f32 - 1.0_f32 + }) * (3.0_f32 / (dim as f32)).sqrt(); + + // initialize the rotation step + let mut rot_step = DMatrix::::identity(dim, dim); + let max_freq = 4; + for n in (0..dim).step_by(2) { + let ang = (PI as f32) * ((n % max_freq) as f32) / (time_res as f32); + let ang_cos = ang.cos(); + let ang_sin = ang.sin(); + rot_step[(n, n)] = ang_cos; + rot_step[(n+1, n)] = ang_sin; + rot_step[(n, n+1)] = -ang_sin; + rot_step[(n+1, n+1)] = ang_cos; + } + + // find the eigenvalues + let mut eigval_series = Vec::, Dyn>>::with_capacity(time_res); + eigval_series.push(rand_mat.complex_eigenvalues()); + for _ in 1..time_res { + rand_mat = &rot_step * rand_mat; + eigval_series.push(rand_mat.complex_eigenvalues()); + } + eigval_series +}*/ + +/* static matrices. should only be used when the dimension is really small */ +/*pub fn rand_eigval_series(time_res: usize) -> Vec, N>> + where + N: ToTypenum + DimName + DimSub, + DefaultAllocator: + Allocator + + Allocator + + Allocator<>::Output> + + Allocator>::Output> +{ + // initialize the random matrix + let dim = N::try_to_usize().unwrap(); + let mut rand_mat = OMatrix::::from_fn(|j, k| { + let n = j*dim + k; + E*((n*n) as f64) % 2.0 - 1.0 + }) * (3.0 / (dim as f64)).sqrt(); + /*let mut rand_mat = OMatrix::::identity();*/ + + // initialize the rotation step + let mut rot_step = OMatrix::::identity(); + let max_freq = 4; + for n in (0..dim).step_by(2) { + let ang = PI * ((n % max_freq) as f64) / (time_res as f64); + let ang_cos = ang.cos(); + let ang_sin = ang.sin(); + rot_step[(n, n)] = ang_cos; + rot_step[(n+1, n)] = ang_sin; + rot_step[(n, n+1)] = -ang_sin; + rot_step[(n+1, n+1)] = ang_cos; + } + + // find the eigenvalues + let mut eigval_series = Vec::, N>>::with_capacity(time_res); + eigval_series.push(rand_mat.complex_eigenvalues()); + for _ in 1..time_res { + rand_mat = &rot_step * rand_mat; + eigval_series.push(rand_mat.complex_eigenvalues()); + } + eigval_series +}*/ \ No newline at end of file diff --git a/lang-trials/rust-benchmark-native/src/main.rs b/lang-trials/rust-benchmark-native/src/main.rs new file mode 100644 index 0000000..1a3a787 --- /dev/null +++ b/lang-trials/rust-benchmark-native/src/main.rs @@ -0,0 +1,110 @@ +// based on Olivier Pelhatre's GTK 3 example, ported to GTK 4 +// +// https://github.com/Ouam74/RUST_Real-time_plots_using_GTK-rs_and_Plotters-rs +// +// a self-contained component might draw on the example below, by StackOverflow +// user Nicolas +// +// https://stackoverflow.com/a/76548487 +// +// here's a crash course in `plotters` +// +// https://plotters-rs.github.io/book/basic/basic_data_plotting.html +// + +extern crate cairo; +use plotters::prelude::*; +use plotters_cairo::CairoBackend; +use gtk::{ + glib, + prelude::*, + Adjustment, + Align, + Application, + ApplicationWindow, + Box, + DrawingArea, + Label, + Orientation, + /*Picture,*/ + Scale +}; +/*use plotters_gtk4::Paintable;*/ +use std::time::Instant; + +mod engine; + +fn main() -> glib::ExitCode { + let app = Application::builder() + .application_id("org.studioinfinity.rust-benchmark-native") + .build(); + + app.connect_activate(|app| { + /* run time is typically 77 ms, but at one point i was consistently + getting more like 260 ms. it's not clear what was causing that. building + from scratch seemed to give the short run time, and waiting a while + since the last build might also have had some effect */ + const TIME_RES: usize = 100; + let start_time = Instant::now(); + let eigval_series = engine::rand_eigval_series(60, TIME_RES); + let run_time = start_time.elapsed().as_millis(); + + // application state + let time_step = Adjustment::new(0.0, 0.0, TIME_RES as f64, 1.0, 0.0, 0.0); + + // create the window. + let window = ApplicationWindow::builder() + .application(app) + .title("The circular law") + .build(); + + // create a vertical box + let container = Box::new(Orientation::Vertical, 5); + window.set_child(Some(&container)); + + // create the run time readout + let run_time_readout = Label::builder() + .margin_top(5) + .margin_start(10) + .halign(Align::Start) + .label(glib::gformat!("{} ms", run_time)) + .build(); + container.append(&run_time_readout); + + // set up the drawing area + let drawing_area = DrawingArea::builder() + .content_width(600) + .content_height(600) + .build(); + let time_step_for_draw = time_step.clone(); + let draw_eigvals = move |_: &DrawingArea, context: &cairo::Context, width: i32, height: i32| { + let root = CairoBackend::new(&context, (width as u32, height as u32)).unwrap().into_drawing_area(); + let _ = root.fill(&BLACK); + + const R_DISP: f64 = 1.5; + let mut chart = ChartBuilder::on(&root) + .build_cartesian_2d(-R_DISP..R_DISP, -R_DISP..R_DISP) + .unwrap(); + let time_step_val = (time_step_for_draw.value() as usize).min(TIME_RES-1); + let eigval_iter = eigval_series[time_step_val].iter(); + let _ = chart.draw_series( + eigval_iter.map(|z| Circle::new((z.re, z.im), 3, WHITE.filled())) + ); + let _ = root.present(); + }; + DrawingAreaExtManual::set_draw_func(&drawing_area, draw_eigvals); + container.append(&drawing_area); + + // set up the time step slider + let time_step_scale = Scale::new(Orientation::Horizontal, Some(&time_step)); + time_step_scale.connect_value_changed(move |_: &Scale| { + drawing_area.queue_draw(); + }); + container.append(&time_step_scale); + + // show the window + window.present(); + }); + + app.run() +} \ No newline at end of file