Merge branch 'woojin' into 'main'

Merge branch 'main' into 'main'

See merge request kaist-cp-class/cs220-private!17
This commit is contained in:
Woojin Lee
2023-08-21 02:50:12 +00:00
6 changed files with 407 additions and 20 deletions

View File

@@ -24,7 +24,7 @@ run_linters || exit 1
for RUNNER in "${RUNNERS[@]}"; do for RUNNER in "${RUNNERS[@]}"; do
echo "Running with $RUNNER..." echo "Running with $RUNNER..."
TESTS=("--lib assignment04_grade") TESTS=("--lib assignment04")
if [ $(run_tests) -ne 0 ]; then if [ $(run_tests) -ne 0 ]; then
exit 1 exit 1
fi fi

View File

@@ -0,0 +1,66 @@
//! Simple matrix multiplication
use itertools::*;
/// elementwise vector addition
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::vec_add;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = vec_add(&vec1, &vec2);
/// assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
/// ```
pub fn vec_add(lhs: &[f64], rhs: &[f64]) -> Vec<f64> {
todo!()
}
/// dot product of two arrays
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::dot_product;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = dot_product(&vec1, &vec2);
///
/// assert_eq!(res, 55.0);
/// ```
pub fn dot_product(lhs: &[f64], rhs: &[f64]) -> f64 {
todo!()
}
/// Matrix multiplication
///
/// Assume rhs is transposed
/// - lhs: (m, n)
/// - rhs: (p, n)
/// - output: (m, p)
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment09::matmul;
///
/// let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
/// let mat2 = vec![
/// vec![7.0, 8.0, 9.0],
/// vec![10.0, 11.0, 12.0],
/// vec![13.0, 14.0, 15.0],
/// vec![16.0, 17.0, 18.0],
/// ];
/// let ans = vec![
/// vec![50.0, 68.0, 86.0, 104.0],
/// vec![122.0, 167.0, 212.0, 257.0],
/// ];
/// let res = matmul(&mat1, &mat2);
/// assert_eq!(ans, res);
/// ```
pub fn matmul(lhs: &[Vec<f64>], rhs: &[Vec<f64>]) -> Vec<Vec<f64>> {
todo!()
}

View File

@@ -0,0 +1,106 @@
#[cfg(test)]
mod test {
use crate::assignments::assignment09::matmul::*;
use approx::*;
use itertools::Itertools;
use ndarray::prelude::*;
use ndarray_rand::{rand_distr::Uniform, RandomExt};
#[test]
fn vec_add_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res = vec_add(&vec1, &vec2);
assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
for _ in 0..5 {
let vec1 = Array::random(500000, Uniform::new(0., 10.));
let vec2 = Array::random(500000, Uniform::new(0., 10.));
let res = vec_add(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let ans = vec1 + vec2;
assert_eq!(Array::from_vec(res), ans);
}
}
#[test]
fn dot_product_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res = dot_product(&vec1, &vec2);
assert_eq!(res, 55.0);
for _ in 0..5 {
let vec1 = Array::random(1000000, Uniform::new(0., 10.));
let vec2 = Array::random(1000000, Uniform::new(0., 10.));
let res = dot_product(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let _res = relative_eq!(res, vec1.dot(&vec2), epsilon = f64::EPSILON);
}
}
/// Reference: <https://github.com/rust-ndarray/ndarray/issues/590>
/// Converts nested `Vec`s to a 2-D array by cloning the elements.
///
/// **Panics** if the length of any axis overflows `isize`, if the
/// size in bytes of all the data overflows `isize`, or if not all the
/// rows have the same length.
fn vec_to_array<T: Clone>(v: Vec<Vec<T>>) -> Array2<T> {
if v.is_empty() {
return Array2::from_shape_vec((0, 0), Vec::new()).unwrap();
}
let nrows = v.len();
let ncols = v[0].len();
let mut data = Vec::with_capacity(nrows * ncols);
for row in &v {
assert_eq!(row.len(), ncols);
data.extend_from_slice(row);
}
Array2::from_shape_vec((nrows, ncols), data).unwrap()
}
#[test]
fn matmul_test() {
let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
let mat2 = vec![
vec![7.0, 8.0, 9.0],
vec![10.0, 11.0, 12.0],
vec![13.0, 14.0, 15.0],
vec![16.0, 17.0, 18.0],
];
let ans = vec![
vec![50.0, 68.0, 86.0, 104.0],
vec![122.0, 167.0, 212.0, 257.0],
];
let res = matmul(&mat1, &mat2);
assert_eq!(ans, res);
for _ in 0..5 {
let mat1 = Array::random((500, 500), Uniform::new(0., 10.));
let mat2 = Array::random((500, 500), Uniform::new(0., 10.));
let ans = mat1.dot(&mat2);
let mat2_transposed = mat2.t();
// Run sequential matrix multiplication
let res = matmul(
mat1.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
mat2_transposed
.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
);
// Check answer
for it in ans.iter().zip(vec_to_array(res).iter()) {
let (ans, res) = it;
let _res = relative_eq!(ans, res);
}
}
}
}

View File

@@ -6,7 +6,9 @@
//! See `assignment09_grade.rs` and `/scripts/grade-09.sh` for the test script. //! See `assignment09_grade.rs` and `/scripts/grade-09.sh` for the test script.
pub mod bigint; pub mod bigint;
pub mod matmul;
pub mod small_exercises; pub mod small_exercises;
mod bigint_grade; mod bigint_grade;
mod matmul_grade;
mod small_exercises_grade; mod small_exercises_grade;

View File

@@ -16,10 +16,10 @@ use rayon::prelude::*;
/// use cs220::assignments::assignment13::sigma; /// use cs220::assignments::assignment13::sigma;
/// use rayon::iter::IntoParallelIterator; /// use rayon::iter::IntoParallelIterator;
/// ///
/// assert_eq!(sigma([1, 2].into_par_iter(), |x| x + 2), 7); /// assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x + 2), 7);
/// assert_eq!(sigma([1, 2].into_par_iter(), |x| x * 4), 12); /// assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x * 4), 12);
/// ``` /// ```
pub fn sigma<T, F: Fn(T) -> i64 + Sync + Send>( pub fn sigma_par<T, F: Fn(T) -> i64 + Sync + Send>(
inner: impl ParallelIterator<Item = T>, inner: impl ParallelIterator<Item = T>,
f: F, f: F,
) -> i64 { ) -> i64 {
@@ -35,14 +35,77 @@ pub fn sigma<T, F: Fn(T) -> i64 + Sync + Send>(
/// use rayon::iter::IntoParallelIterator; /// use rayon::iter::IntoParallelIterator;
/// ///
/// assert_eq!( /// assert_eq!(
/// interleave3([1, 2].into_par_iter(), [3, 4].into_par_iter(), [5, 6].into_par_iter()), /// interleave3_par([1, 2].into_par_iter(), [3, 4].into_par_iter(), [5, 6].into_par_iter()),
/// vec![1, 3, 5, 2, 4, 6] /// vec![1, 3, 5, 2, 4, 6]
/// ); /// );
/// ``` /// ```
pub fn interleave3<T: Send>( pub fn interleave3_par<T: Send>(
list1: impl IndexedParallelIterator<Item = T>, list1: impl IndexedParallelIterator<Item = T>,
list2: impl IndexedParallelIterator<Item = T>, list2: impl IndexedParallelIterator<Item = T>,
list3: impl IndexedParallelIterator<Item = T>, list3: impl IndexedParallelIterator<Item = T>,
) -> Vec<T> { ) -> Vec<T> {
todo!() todo!()
} }
/// Parallel vector addition
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment13::vec_add_par;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = vec_add_par(&vec1, &vec2);
/// assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
/// ```
pub fn vec_add_par(lhs: &[f64], rhs: &[f64]) -> Vec<f64> {
todo!()
}
/// Parallel dot product of two arrays
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment13::dot_product_par;
///
/// let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
/// let res = dot_product_par(&vec1, &vec2);
///
/// assert_eq!(res, 55.0);
/// ```
pub fn dot_product_par(lhs: &[f64], rhs: &[f64]) -> f64 {
todo!()
}
/// Parallel Matrix multiplication
///
/// Assume rhs is transposed
/// - lhs: (m, n)
/// - rhs: (p, n)
/// - output: (m, p)
///
/// # Exmaple
///
/// ```
/// use cs220::assignments::assignment13::matmul_par;
///
/// let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
/// let mat2 = vec![
/// vec![7.0, 8.0, 9.0],
/// vec![10.0, 11.0, 12.0],
/// vec![13.0, 14.0, 15.0],
/// vec![16.0, 17.0, 18.0],
/// ];
/// let ans = vec![
/// vec![50.0, 68.0, 86.0, 104.0],
/// vec![122.0, 167.0, 212.0, 257.0],
/// ];
/// let res = matmul_par(&mat1, &mat2);
/// assert_eq!(ans, res);
/// ```
pub fn matmul_par(lhs: &[Vec<f64>], rhs: &[Vec<f64>]) -> Vec<Vec<f64>> {
todo!()
}

View File

@@ -1,42 +1,48 @@
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::super::assignment09::matmul::*;
use super::super::assignment13::*; use super::super::assignment13::*;
use approx::*;
use itertools::Itertools;
use ndarray::prelude::*;
use ndarray_rand::{rand_distr::Uniform, RandomExt};
use rayon::prelude::IntoParallelIterator; use rayon::prelude::IntoParallelIterator;
use std::time::Instant;
#[test] #[test]
fn test_sigma() { fn test_sigma_par() {
assert_eq!(sigma([].into_par_iter(), |x: i64| x * 2), 0); assert_eq!(sigma_par([].into_par_iter(), |x: i64| x * 2), 0);
assert_eq!(sigma([1].into_par_iter(), |x| x * 3), 3); assert_eq!(sigma_par([1].into_par_iter(), |x| x * 3), 3);
assert_eq!(sigma([1, 2].into_par_iter(), |x| x + 2), 7); assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x + 2), 7);
assert_eq!(sigma([1, 2].into_par_iter(), |x| x * 4), 12); assert_eq!(sigma_par([1, 2].into_par_iter(), |x| x * 4), 12);
assert_eq!(sigma([1, 2, 3].into_par_iter(), |x| x * 5), 30); assert_eq!(sigma_par([1, 2, 3].into_par_iter(), |x| x * 5), 30);
assert_eq!( assert_eq!(
sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.floor() sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.floor()
as i64), as i64),
10 10
); );
assert_eq!( assert_eq!(
sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.ceil() sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.ceil()
as i64), as i64),
13 13
); );
assert_eq!( assert_eq!(
sigma([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.round() sigma_par([-1.2, 3.0, 4.2, 5.8].into_par_iter(), |x: f64| x.round()
as i64), as i64),
12 12
); );
assert_eq!( assert_eq!(
sigma(["Hello,", "World!"].into_par_iter(), |x| x.len() as i64), sigma_par(["Hello,", "World!"].into_par_iter(), |x| x.len() as i64),
12 12
); );
} }
#[test] #[test]
fn test_interleave3() { fn test_interleave3_par() {
assert_eq!( assert_eq!(
interleave3( interleave3_par(
[1, 2].into_par_iter(), [1, 2].into_par_iter(),
[3, 4].into_par_iter(), [3, 4].into_par_iter(),
[5, 6].into_par_iter() [5, 6].into_par_iter()
@@ -45,7 +51,7 @@ mod test {
); );
assert_eq!( assert_eq!(
interleave3( interleave3_par(
[1, 2, 3].into_par_iter(), [1, 2, 3].into_par_iter(),
[4, 5, 6].into_par_iter(), [4, 5, 6].into_par_iter(),
[7, 8, 9].into_par_iter() [7, 8, 9].into_par_iter()
@@ -54,7 +60,7 @@ mod test {
); );
assert_eq!( assert_eq!(
interleave3( interleave3_par(
["a", "b", "c"].into_par_iter(), ["a", "b", "c"].into_par_iter(),
["d", "e", "f"].into_par_iter(), ["d", "e", "f"].into_par_iter(),
["g", "h", "i"].into_par_iter() ["g", "h", "i"].into_par_iter()
@@ -64,4 +70,148 @@ mod test {
"adgbehcfi" "adgbehcfi"
); );
} }
#[test]
fn vec_add_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res = vec_add(&vec1, &vec2);
assert_eq!(res, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
for _ in 0..5 {
let vec1 = Array::random(500000, Uniform::new(0., 10.));
let vec2 = Array::random(500000, Uniform::new(0., 10.));
let now_seq = Instant::now();
let res_seq = vec_add(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let elapsed_seq = now_seq.elapsed();
let now_par = Instant::now();
let res_par = vec_add_par(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let elapsed_par = now_par.elapsed();
let ans = vec1 + vec2;
assert_eq!(Array::from_vec(res_seq), ans);
assert_eq!(Array::from_vec(res_par), ans);
assert!(elapsed_par < elapsed_seq);
}
}
#[test]
fn dot_product_test() {
let vec1 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let vec2 = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let res_seq = dot_product(&vec1, &vec2);
let res_par = dot_product_par(&vec1, &vec2);
assert_eq!(res_seq, 55.0);
assert_eq!(res_par, 55.0);
for _ in 0..5 {
let vec1 = Array::random(1000000, Uniform::new(0., 10.));
let vec2 = Array::random(1000000, Uniform::new(0., 10.));
let now_seq = Instant::now();
let res_seq = dot_product(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let elapsed_seq = now_seq.elapsed();
let now_par = Instant::now();
let res_par = dot_product_par(vec1.as_slice().unwrap(), vec2.as_slice().unwrap());
let elapsed_par = now_par.elapsed();
let _res = relative_eq!(res_seq, vec1.dot(&vec2), epsilon = f64::EPSILON);
let _res = relative_eq!(res_par, vec1.dot(&vec2), epsilon = f64::EPSILON);
assert!(elapsed_par < elapsed_seq);
}
}
/// Reference: <https://github.com/rust-ndarray/ndarray/issues/590>
/// Converts nested `Vec`s to a 2-D array by cloning the elements.
///
/// **Panics** if the length of any axis overflows `isize`, if the
/// size in bytes of all the data overflows `isize`, or if not all the
/// rows have the same length.
fn vec_to_array<T: Clone>(v: Vec<Vec<T>>) -> Array2<T> {
if v.is_empty() {
return Array2::from_shape_vec((0, 0), Vec::new()).unwrap();
}
let nrows = v.len();
let ncols = v[0].len();
let mut data = Vec::with_capacity(nrows * ncols);
for row in &v {
assert_eq!(row.len(), ncols);
data.extend_from_slice(row);
}
Array2::from_shape_vec((nrows, ncols), data).unwrap()
}
#[test]
fn matmul_test() {
let mat1 = vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]];
let mat2 = vec![
vec![7.0, 8.0, 9.0],
vec![10.0, 11.0, 12.0],
vec![13.0, 14.0, 15.0],
vec![16.0, 17.0, 18.0],
];
let ans = vec![
vec![50.0, 68.0, 86.0, 104.0],
vec![122.0, 167.0, 212.0, 257.0],
];
let res_seq = matmul(&mat1, &mat2);
let res_par = matmul_par(&mat1, &mat2);
assert_eq!(ans, res_seq);
assert_eq!(ans, res_par);
for _ in 0..5 {
let mat1 = Array::random((500, 500), Uniform::new(0., 10.));
let mat2 = Array::random((500, 500), Uniform::new(0., 10.));
let ans = mat1.dot(&mat2);
let mat2_transposed = mat2.t();
// Run sequential matrix multiplication
let now_seq = Instant::now();
let res_seq = matmul(
mat1.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
mat2_transposed
.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
);
let elapsed_seq = now_seq.elapsed();
// Run parallel matrix multiplication
let now_par = Instant::now();
let res_par = matmul_par(
mat1.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
mat2_transposed
.axis_iter(Axis(0))
.map(|row| row.to_vec())
.collect::<Vec<_>>()
.as_slice(),
);
let elapsed_par = now_par.elapsed();
// Check answer
for it in ans.iter().zip(vec_to_array(res_seq).iter()) {
let (ans, seq) = it;
let _res = relative_eq!(ans, seq);
}
for it in ans.iter().zip(vec_to_array(res_par).iter()) {
let (ans, par) = it;
let _res = relative_eq!(ans, par);
}
// Check time
// println!("Sequential: {:?}", elapsed_seq);
// println!("Parallel: {:?}", elapsed_par);
assert!(elapsed_par < elapsed_seq);
}
}
} }