Skip to content

Commit

Permalink
refactor(laplace): migrate to ndarray
Browse files Browse the repository at this point in the history
  • Loading branch information
mkroening committed Dec 17, 2024
1 parent c9193a8 commit 26294a5
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 60 deletions.
1 change: 1 addition & 0 deletions examples/demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["Stefan Lankes <[email protected]>"]
edition = "2021"

[dependencies]
ndarray = { version = "0.16", features = ["rayon"] }
rayon = "1.5"

[target.'cfg(target_os = "hermit")'.dependencies]
Expand Down
110 changes: 50 additions & 60 deletions examples/demo/src/laplace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
//!
//! This module performs the Jacobi method for solving Laplace's differential equation.
#![allow(clippy::reversed_empty_ranges)]

use std::mem;
use std::time::Instant;
use std::{mem, vec};

use ndarray::{s, Array2, ArrayView2, ArrayViewMut2};
use rayon::prelude::*;

const SIZE: usize = if cfg!(debug_assertions) { 16 } else { 64 };
Expand All @@ -17,89 +20,76 @@ pub fn laplace() {

eprintln!("Laplace iterations");
let now = Instant::now();
let residual = compute(&mut matrix, SIZE, SIZE, ITERATIONS);
let residual = compute(matrix.view_mut(), ITERATIONS);
let elapsed = now.elapsed();
eprintln!("{ITERATIONS} iterations: {elapsed:?} (residual: {residual})");

assert!(residual < 0.001);
}

fn matrix_setup(size_x: usize, size_y: usize) -> vec::Vec<f64> {
let mut matrix = vec![0.0; size_x * size_y];

// top row
for f in matrix.iter_mut().take(size_x) {
*f = 1.0;
}

// bottom row
for x in 0..size_x {
matrix[(size_y - 1) * size_x + x] = 1.0;
}

// left row
for y in 0..size_y {
matrix[y * size_x] = 1.0;
}
fn matrix_setup(size_x: usize, size_y: usize) -> Array2<f64> {
let mut matrix = Array2::zeros((size_x, size_y));

// right row
for y in 0..size_y {
matrix[y * size_x + size_x - 1] = 1.0;
}
matrix.row_mut(0).fill(1.0);
matrix.row_mut(size_x - 1).fill(1.0);
matrix.column_mut(0).fill(1.0);
matrix.column_mut(size_x - 1).fill(1.0);

matrix
}

fn get_residual(matrix: &[f64], size_x: usize, size_y: usize) -> f64 {
(1..size_y - 1)
fn get_residual(matrix: ArrayView2<f64>) -> f64 {
matrix
.slice(s![1..-1, 1..-1])
.outer_iter()
.into_par_iter()
.map(|y| {
let mut local_sum = 0.0;

for x in 1..(size_x - 1) {
let new = (matrix[y * size_x + x - 1]
+ matrix[y * size_x + x + 1]
+ matrix[(y + 1) * size_x + x]
+ matrix[(y - 1) * size_x + x])
* 0.25;

let diff = new - matrix[y * size_x + x];
local_sum += diff * diff;
}

local_sum
.enumerate()
.map(|(i, row)| {
let i = i + 1; // To compensate slicing
row.iter()
.enumerate()
.map(|(j, cell)| {
let j = j + 1; // To compensate slicing
let sum = matrix[[i, j - 1]]
+ matrix[[i, j + 1]]
+ matrix[[i - 1, j]]
+ matrix[[i + 1, j]];
let new = sum * 0.25;
(new - *cell).powi(2)
})
.sum::<f64>()
})
.sum()
}

fn iteration(cur: &[f64], next: &mut [f64], size_x: usize, size_y: usize) {
next.par_chunks_mut(size_y)
.enumerate() // to figure out where this chunk came from
.for_each(|(chunk_index, slice)| {
if chunk_index > 0 && chunk_index < size_y - 1 {
let offset_base = chunk_index * size_x;

for x in 1..size_x - 1 {
slice[x] = (cur[offset_base + x - 1]
+ cur[offset_base + x + 1]
+ cur[offset_base + size_x + x]
+ cur[offset_base - size_x + x])
* 0.25;
}
fn iteration(current: ArrayView2<f64>, mut next: ArrayViewMut2<f64>) {
next.slice_mut(s![1..-1, 1..-1])
.outer_iter_mut()
.into_par_iter()
.enumerate()
.for_each(|(i, mut row)| {
let i = i + 1; // To compensate slicing
for (j, cell) in row.iter_mut().enumerate() {
let j = j + 1; // To compensate slicing
let sum = current[[i, j - 1]]
+ current[[i, j + 1]]
+ current[[i - 1, j]]
+ current[[i + 1, j]];
*cell = sum * 0.25;
}
});
}

fn compute(matrix: &mut [f64], size_x: usize, size_y: usize, iterations: usize) -> f64 {
let mut clone = matrix.to_vec();
fn compute(mut matrix: ArrayViewMut2<'_, f64>, iterations: usize) -> f64 {
let mut owned = matrix.to_owned();

let mut current = matrix;
let mut next = &mut clone[..];
let mut current = matrix.view_mut();
let mut next = owned.view_mut();

for _ in 0..iterations {
iteration(current, next, size_x, size_y);
iteration(current.view(), next.view_mut());
mem::swap(&mut current, &mut next);
}

get_residual(current, size_x, size_y)
get_residual(current.view())
}

0 comments on commit 26294a5

Please sign in to comment.