From 0dc6f25c3219c679b6f3c1a851f3d6682df647fe Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:04:48 +0200 Subject: [PATCH 01/21] Docs: added documentation to marginal ranking loss function --- .../loss_function/marginal_ranking.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/machine_learning/loss_function/marginal_ranking.rs diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs new file mode 100644 index 00000000000..331c23a9ada --- /dev/null +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -0,0 +1,12 @@ +// Marginal Ranking +// +// The 'mrg_ranking_loss' function calculates the Marginal Ranking loss, which is a +// loss function used for ranking problems in machine learning. +// +// ## Formula +// +// For a pair of values `x_first` and `x_second`, `margin`, and `y_true`, +// the Marginal Ranking loss is calculated as: +// +// - loss = `max(0, -y_true * (x_first - x_second) + margin)`. +// From d771a445ee07dcb9b9459c13c6d610d253e869a1 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:08:24 +0200 Subject: [PATCH 02/21] Feat: created function signature --- src/machine_learning/loss_function/marginal_ranking.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 331c23a9ada..eb7059bf413 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -10,3 +10,5 @@ // // - loss = `max(0, -y_true * (x_first - x_second) + margin)`. // + +pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: i8) -> f64 {} From 6c152f518838992062157eb4db392f4a9080bdc7 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:13:11 +0200 Subject: [PATCH 03/21] Doc: added return comment --- src/machine_learning/loss_function/marginal_ranking.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index eb7059bf413..1c6cae52a9e 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -10,5 +10,7 @@ // // - loss = `max(0, -y_true * (x_first - x_second) + margin)`. // +// It returns the average loss by dividing the `total_loss` by total no. of +// elements. pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: i8) -> f64 {} From d94e3ef9019ab830b7df707f9fb26c3d290256ad Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:18:38 +0200 Subject: [PATCH 04/21] Feat: finished implementation and changed type of input --- src/machine_learning/loss_function/marginal_ranking.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 1c6cae52a9e..53491769d36 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -13,4 +13,11 @@ // It returns the average loss by dividing the `total_loss` by total no. of // elements. -pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: i8) -> f64 {} +pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: f64) -> f64 { + let mut total_loss: f64 = 0.0; + for (f, s) in x_first.iter().zip(x_second.iter()) { + let loss: f64 = (margin - y_true * (f - s)).max(0.0); + total_loss += loss; + } + total_loss / (x_first.len() as f64) +} From 9a2964646936ce11f0a899dc5ee7e2aab16efea7 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:23:25 +0200 Subject: [PATCH 05/21] Test: created a test case --- .../loss_function/marginal_ranking.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 53491769d36..9f2d32bc282 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -21,3 +21,20 @@ pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: } total_loss / (x_first.len() as f64) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_marginal_ranking_loss() { + let first_values: Vec = vec![1.0, 2.0, 3.0]; + let second_values: Vec = vec![2.0, 3.0, 4.0]; + let margin: f64 = 1.0; + let actual_value: f64 = -1.0; + assert_eq!( + mrg_ranking_loss(&first_values, &second_values, margin, actual_value), + 2.0 + ); + } +} From 30f8b05ac25902d3d21461e4659ee6b512f4f5fc Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Fri, 31 May 2024 14:29:32 +0200 Subject: [PATCH 06/21] Feat: added the correct exports --- src/machine_learning/loss_function/marginal_ranking.rs | 2 +- src/machine_learning/loss_function/mod.rs | 2 ++ src/machine_learning/mod.rs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 9f2d32bc282..a439a11357a 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -34,7 +34,7 @@ mod tests { let actual_value: f64 = -1.0; assert_eq!( mrg_ranking_loss(&first_values, &second_values, margin, actual_value), - 2.0 + 0.0 ); } } diff --git a/src/machine_learning/loss_function/mod.rs b/src/machine_learning/loss_function/mod.rs index c0196666dbf..da28315d066 100644 --- a/src/machine_learning/loss_function/mod.rs +++ b/src/machine_learning/loss_function/mod.rs @@ -1,11 +1,13 @@ mod hinge_loss; mod huber_loss; mod kl_divergence_loss; +mod marginal_ranking; mod mean_absolute_error_loss; mod mean_squared_error_loss; pub use self::hinge_loss::hng_loss; pub use self::huber_loss::huber_loss; pub use self::kl_divergence_loss::kld_loss; +pub use self::marginal_ranking::mrg_ranking_loss; pub use self::mean_absolute_error_loss::mae_loss; pub use self::mean_squared_error_loss::mse_loss; diff --git a/src/machine_learning/mod.rs b/src/machine_learning/mod.rs index a88a811e0ba..8d12c3ebca0 100644 --- a/src/machine_learning/mod.rs +++ b/src/machine_learning/mod.rs @@ -11,6 +11,7 @@ pub use self::loss_function::hng_loss; pub use self::loss_function::huber_loss; pub use self::loss_function::kld_loss; pub use self::loss_function::mae_loss; +pub use self::loss_function::mrg_ranking_loss; pub use self::loss_function::mse_loss; pub use self::optimization::gradient_descent; pub use self::optimization::Adam; From 7f5d3e4bf39f878b501701e318f02d5d8ab78a94 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Mon, 10 Jun 2024 15:40:09 +0200 Subject: [PATCH 07/21] Feat: now using option as a return type --- .../loss_function/marginal_ranking.rs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index a439a11357a..46e1abdd09b 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -13,13 +13,28 @@ // It returns the average loss by dividing the `total_loss` by total no. of // elements. -pub fn mrg_ranking_loss(x_first: &[f64], x_second: &[f64], margin: f64, y_true: f64) -> f64 { +pub fn mrg_ranking_loss( + x_first: &[f64], + x_second: &[f64], + margin: f64, + y_true: f64, +) -> Option { + if x_first.len() != x_second.len() || x_first.is_empty() || x_second.is_empty() { + return None; + } + if margin < 0.0 { + return None; + } + if y_true != 1.0 && y_true != -1.0 { + return None; + } + let mut total_loss: f64 = 0.0; for (f, s) in x_first.iter().zip(x_second.iter()) { let loss: f64 = (margin - y_true * (f - s)).max(0.0); total_loss += loss; } - total_loss / (x_first.len() as f64) + Some(total_loss / (x_first.len() as f64)) } #[cfg(test)] @@ -34,7 +49,7 @@ mod tests { let actual_value: f64 = -1.0; assert_eq!( mrg_ranking_loss(&first_values, &second_values, margin, actual_value), - 0.0 + Some(0.0) ); } } From b0e552a78b4b4858a8f946ebc177837cb97d6733 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Mon, 10 Jun 2024 15:41:43 +0200 Subject: [PATCH 08/21] Test: added a macro for testing purposes as suggested --- .../loss_function/marginal_ranking.rs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 46e1abdd09b..0b4762d44db 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -41,15 +41,25 @@ pub fn mrg_ranking_loss( mod tests { use super::*; - #[test] - fn test_marginal_ranking_loss() { - let first_values: Vec = vec![1.0, 2.0, 3.0]; - let second_values: Vec = vec![2.0, 3.0, 4.0]; - let margin: f64 = 1.0; - let actual_value: f64 = -1.0; - assert_eq!( - mrg_ranking_loss(&first_values, &second_values, margin, actual_value), - Some(0.0) - ); + macro_rules! test_mrg_ranking_loss { + ($($name:ident: $test_case:expr,)*) => { + $( + #[test] + fn $name() { + let (x_first, x_second, margin, y_true, expected) = $test_case; + let result = mrg_ranking_loss(&x_first, &x_second, margin, y_true); + assert_eq!(result, expected); + } + )* + }; + } + + test_mrg_ranking_loss! { + test_simple_ranking_example: (vec![3.0, 5.0, 2.0], vec![2.0, 4.0, 1.0], 1.0, 1.0, Some(0.0)), + test_negative_margin: (vec![1.0, 2.0, 3.0], vec![3.0, 2.0, 1.0], 0.5, -1.0, Some(1.0)), + test_identical_scores: (vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0], 1.0, 1.0, Some(1.0)), + test_mixed_y_true: (vec![3.0, 5.0, 7.0], vec![2.0, 6.0, 1.0], 1.0, -1.0, Some(3.0)), + test_different_lengths: (vec![1.0, 2.0], vec![3.0], 1.0, 1.0, None), + test_empty_vectors: (vec![], vec![], 1.0, 1.0, None), } } From 2fe3de77e991e50bf3e4e0c750018d29d5c892e1 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Tue, 11 Jun 2024 12:59:01 +0200 Subject: [PATCH 09/21] Test: macro tests took too long --- .../loss_function/marginal_ranking.rs | 80 ++++++++++++++----- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 0b4762d44db..8661c2d5202 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -41,25 +41,69 @@ pub fn mrg_ranking_loss( mod tests { use super::*; - macro_rules! test_mrg_ranking_loss { - ($($name:ident: $test_case:expr,)*) => { - $( - #[test] - fn $name() { - let (x_first, x_second, margin, y_true, expected) = $test_case; - let result = mrg_ranking_loss(&x_first, &x_second, margin, y_true); - assert_eq!(result, expected); - } - )* - }; + #[test] + fn test_marginal_ranking_loss() { + let first_values: Vec = vec![1.0, 2.0, 3.0]; + let second_values: Vec = vec![2.0, 3.0, 4.0]; + let margin: f64 = 1.0; + let actual_value: f64 = -1.0; + assert_eq!( + mrg_ranking_loss(&first_values, &second_values, margin, actual_value), + Some(0.0) + ); } - test_mrg_ranking_loss! { - test_simple_ranking_example: (vec![3.0, 5.0, 2.0], vec![2.0, 4.0, 1.0], 1.0, 1.0, Some(0.0)), - test_negative_margin: (vec![1.0, 2.0, 3.0], vec![3.0, 2.0, 1.0], 0.5, -1.0, Some(1.0)), - test_identical_scores: (vec![1.0, 1.0, 1.0], vec![1.0, 1.0, 1.0], 1.0, 1.0, Some(1.0)), - test_mixed_y_true: (vec![3.0, 5.0, 7.0], vec![2.0, 6.0, 1.0], 1.0, -1.0, Some(3.0)), - test_different_lengths: (vec![1.0, 2.0], vec![3.0], 1.0, 1.0, None), - test_empty_vectors: (vec![], vec![], 1.0, 1.0, None), + #[test] + fn test_marginal_ranking_loss_invalid_length0() { + let x_first: Vec = vec![1.0, 2.0, 3.0]; + let x_second: Vec = vec![2.0, 3.0]; + let margin: f64 = 1.0; + let y_true: f64 = 1.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + } + + #[test] + fn test_marginal_ranking_loss_invalid_length1() { + let x_first: Vec = vec![1.0, 2.0]; + let x_second: Vec = vec![2.0, 3.0, 4.0]; + let margin: f64 = 1.0; + let y_true: f64 = 1.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + } + + #[test] + fn test_marginal_ranking_invalid_values() { + let x_first: Vec = vec![1.0, 2.0, 3.0]; + let x_second: Vec = vec![2.0, 3.0, 4.0]; + let margin: f64 = -1.0; + let y_true: f64 = 1.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + } + + #[test] + fn test_marginal_ranking_invalid_y_true() { + let x_first: Vec = vec![1.0, 2.0, 3.0]; + let x_second: Vec = vec![2.0, 3.0, 4.0]; + let margin: f64 = 1.0; + let y_true: f64 = 2.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + } + + #[test] + fn test_marginal_ranking_empty_prediction0() { + let x_first: Vec = vec![]; + let x_second: Vec = vec![1.0, 2.0, 3.0]; + let margin: f64 = 1.0; + let y_true: f64 = 1.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + } + + #[test] + fn test_marginal_ranking_empty_prediction1() { + let x_first: Vec = vec![1.0, 2.0, 3.0]; + let x_second: Vec = vec![]; + let margin: f64 = 1.0; + let y_true: f64 = 1.0; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); } } From 44a0813124e915d620db284b88ff23185f8ba04e Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Wed, 19 Jun 2024 15:49:02 +0200 Subject: [PATCH 10/21] Docs: added the algorithm to directory with the correct link --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 220e7815dda..4db1d4032df 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -155,6 +155,7 @@ * [Hinge Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/hinge_loss.rs) * [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/huber_loss.rs) * [Kl Divergence Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/kl_divergence_loss.rs) + * [Marginal Ranking Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/marginal_ranking.rs) * [Mean Absolute Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_absolute_error_loss.rs) * [Mean Squared Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_squared_error_loss.rs) * Optimization From 090b50c298b529b68f276a6d7c364d650b89a538 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 11 Jul 2024 12:50:42 +0200 Subject: [PATCH 11/21] Feat: algorithm now returns Result and updated tests --- .../loss_function/marginal_ranking.rs | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 8661c2d5202..a13d8902ec4 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -18,15 +18,18 @@ pub fn mrg_ranking_loss( x_second: &[f64], margin: f64, y_true: f64, -) -> Option { - if x_first.len() != x_second.len() || x_first.is_empty() || x_second.is_empty() { - return None; +) -> Result { + if x_first.len() != x_second.len() { + return Err(MarginalRankingLossError::InputsHaveDifferentLength); + } + if x_first.is_empty() || x_second.is_empty() { + return Err(MarginalRankingLossError::EmptyInputs); } if margin < 0.0 { - return None; + return Err(MarginalRankingLossError::InvalidValues); } if y_true != 1.0 && y_true != -1.0 { - return None; + return Err(MarginalRankingLossError::InvalidValues); } let mut total_loss: f64 = 0.0; @@ -34,7 +37,14 @@ pub fn mrg_ranking_loss( let loss: f64 = (margin - y_true * (f - s)).max(0.0); total_loss += loss; } - Some(total_loss / (x_first.len() as f64)) + Ok(total_loss / (x_first.len() as f64)) +} + +#[derive(Debug, PartialEq, Eq)] +pub enum MarginalRankingLossError { + InputsHaveDifferentLength, + EmptyInputs, + InvalidValues, } #[cfg(test)] @@ -49,7 +59,7 @@ mod tests { let actual_value: f64 = -1.0; assert_eq!( mrg_ranking_loss(&first_values, &second_values, margin, actual_value), - Some(0.0) + Ok(0.0) ); } @@ -59,7 +69,10 @@ mod tests { let x_second: Vec = vec![2.0, 3.0]; let margin: f64 = 1.0; let y_true: f64 = 1.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InputsHaveDifferentLength) + ); } #[test] @@ -68,7 +81,10 @@ mod tests { let x_second: Vec = vec![2.0, 3.0, 4.0]; let margin: f64 = 1.0; let y_true: f64 = 1.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InputsHaveDifferentLength) + ); } #[test] @@ -77,7 +93,10 @@ mod tests { let x_second: Vec = vec![2.0, 3.0, 4.0]; let margin: f64 = -1.0; let y_true: f64 = 1.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InvalidValues) + ); } #[test] @@ -86,7 +105,10 @@ mod tests { let x_second: Vec = vec![2.0, 3.0, 4.0]; let margin: f64 = 1.0; let y_true: f64 = 2.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InvalidValues) + ); } #[test] @@ -95,7 +117,10 @@ mod tests { let x_second: Vec = vec![1.0, 2.0, 3.0]; let margin: f64 = 1.0; let y_true: f64 = 1.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InputsHaveDifferentLength) + ); } #[test] @@ -104,6 +129,9 @@ mod tests { let x_second: Vec = vec![]; let margin: f64 = 1.0; let y_true: f64 = 1.0; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), None); + assert_eq!( + mrg_ranking_loss(&x_first, &x_second, margin, y_true), + Err(MarginalRankingLossError::InputsHaveDifferentLength) + ); } } From 7dae97234efca6605aa1c5fc90d7acda0d47b7a5 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 11 Jul 2024 13:11:20 +0200 Subject: [PATCH 12/21] Test: added a macro for testing purposes and more tests --- .../loss_function/marginal_ranking.rs | 111 ++++++------------ 1 file changed, 34 insertions(+), 77 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index a13d8902ec4..5d10c8c9157 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -22,7 +22,7 @@ pub fn mrg_ranking_loss( if x_first.len() != x_second.len() { return Err(MarginalRankingLossError::InputsHaveDifferentLength); } - if x_first.is_empty() || x_second.is_empty() { + if x_first.is_empty() { return Err(MarginalRankingLossError::EmptyInputs); } if margin < 0.0 { @@ -51,87 +51,44 @@ pub enum MarginalRankingLossError { mod tests { use super::*; - #[test] - fn test_marginal_ranking_loss() { - let first_values: Vec = vec![1.0, 2.0, 3.0]; - let second_values: Vec = vec![2.0, 3.0, 4.0]; - let margin: f64 = 1.0; - let actual_value: f64 = -1.0; - assert_eq!( - mrg_ranking_loss(&first_values, &second_values, margin, actual_value), - Ok(0.0) - ); + macro_rules! test_with_wrong_inputs { + ($($name:ident: $inputs:expr,)*) => { + $( + #[test] + fn $name() { + let (x_first, x_second, margin, y_true, expected) = $inputs; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), expected); + } + )* + } } - #[test] - fn test_marginal_ranking_loss_invalid_length0() { - let x_first: Vec = vec![1.0, 2.0, 3.0]; - let x_second: Vec = vec![2.0, 3.0]; - let margin: f64 = 1.0; - let y_true: f64 = 1.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InputsHaveDifferentLength) - ); + test_with_wrong_inputs! { + invalid_length0: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), + invalid_length1: (vec![1.0, 2.0], vec![2.0, 3.0, 4.0], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), + invalid_length2: (vec![], vec![1.0, 2.0, 3.0], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), + invalid_length3: (vec![1.0, 2.0, 3.0], vec![], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), + invalid_values: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], -1.0, 1.0, Err(MarginalRankingLossError::InvalidValues)), + invalid_y_true: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, 2.0, Err(MarginalRankingLossError::InvalidValues)), + empty_inputs: (vec![], vec![], 1.0, 1.0, Err(MarginalRankingLossError::EmptyInputs)), } - #[test] - fn test_marginal_ranking_loss_invalid_length1() { - let x_first: Vec = vec![1.0, 2.0]; - let x_second: Vec = vec![2.0, 3.0, 4.0]; - let margin: f64 = 1.0; - let y_true: f64 = 1.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InputsHaveDifferentLength) - ); + macro_rules! test_marginal_ranking_loss { + ($($name:ident: $inputs:expr,)*) => { + $( + #[test] + fn $name() { + let (x_first, x_second, margin, y_true, expected) = $inputs; + assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), Ok(expected)); + } + )* + } } - #[test] - fn test_marginal_ranking_invalid_values() { - let x_first: Vec = vec![1.0, 2.0, 3.0]; - let x_second: Vec = vec![2.0, 3.0, 4.0]; - let margin: f64 = -1.0; - let y_true: f64 = 1.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InvalidValues) - ); - } - - #[test] - fn test_marginal_ranking_invalid_y_true() { - let x_first: Vec = vec![1.0, 2.0, 3.0]; - let x_second: Vec = vec![2.0, 3.0, 4.0]; - let margin: f64 = 1.0; - let y_true: f64 = 2.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InvalidValues) - ); - } - - #[test] - fn test_marginal_ranking_empty_prediction0() { - let x_first: Vec = vec![]; - let x_second: Vec = vec![1.0, 2.0, 3.0]; - let margin: f64 = 1.0; - let y_true: f64 = 1.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InputsHaveDifferentLength) - ); - } - - #[test] - fn test_marginal_ranking_empty_prediction1() { - let x_first: Vec = vec![1.0, 2.0, 3.0]; - let x_second: Vec = vec![]; - let margin: f64 = 1.0; - let y_true: f64 = 1.0; - assert_eq!( - mrg_ranking_loss(&x_first, &x_second, margin, y_true), - Err(MarginalRankingLossError::InputsHaveDifferentLength) - ); + test_marginal_ranking_loss! { + set_0: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, -1.0, 0.0), + set_1: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, 1.0, 2.0), + set_2: (vec![1.0, 2.0, 3.0], vec![1.0, 2.0, 3.0], 0.0, 1.0, 0.0), + set_3: (vec![4.0, 5.0, 6.0], vec![1.0, 2.0, 3.0], 1.0, -1.0, 4.0), } } From 0bb2dc1e42fed2389723b3a76d55e348e7cc33e8 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 11 Jul 2024 13:27:36 +0200 Subject: [PATCH 13/21] Docs: added more documentation to the file --- .../loss_function/marginal_ranking.rs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 5d10c8c9157..49786f1d8ce 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -1,17 +1,23 @@ -// Marginal Ranking -// -// The 'mrg_ranking_loss' function calculates the Marginal Ranking loss, which is a -// loss function used for ranking problems in machine learning. -// -// ## Formula -// -// For a pair of values `x_first` and `x_second`, `margin`, and `y_true`, -// the Marginal Ranking loss is calculated as: -// -// - loss = `max(0, -y_true * (x_first - x_second) + margin)`. -// -// It returns the average loss by dividing the `total_loss` by total no. of -// elements. +/// Marginal Ranking +/// +/// The 'mrg_ranking_loss' function calculates the Marginal Ranking loss, which is a +/// loss function used for ranking problems in machine learning. +/// +/// ## Formula +/// +/// For a pair of values `x_first` and `x_second`, `margin`, and `y_true`, +/// the Marginal Ranking loss is calculated as: +/// +/// - loss = `max(0, -y_true * (x_first - x_second) + margin)`. +/// +/// It returns the average loss by dividing the `total_loss` by total no. of +/// elements. +/// +/// Pytorch implementation: +/// https://pytorch.org/docs/stable/generated/torch.nn.MarginRankingLoss.html +/// https://gombru.github.io/2019/04/03/ranking_loss/ +/// https://vinija.ai/concepts/loss/#pairwise-ranking-loss +/// pub fn mrg_ranking_loss( x_first: &[f64], From 361d64f2be1da1ecd14e3d0bb8c09993669aae6a Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 18 Jul 2024 11:27:18 +0200 Subject: [PATCH 14/21] Refcator: changed the name of the function --- src/machine_learning/loss_function/marginal_ranking.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 49786f1d8ce..234a8c989c2 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -1,6 +1,6 @@ /// Marginal Ranking /// -/// The 'mrg_ranking_loss' function calculates the Marginal Ranking loss, which is a +/// The 'marginal_ranking_loss' function calculates the Marginal Ranking loss, which is a /// loss function used for ranking problems in machine learning. /// /// ## Formula @@ -19,7 +19,7 @@ /// https://vinija.ai/concepts/loss/#pairwise-ranking-loss /// -pub fn mrg_ranking_loss( +pub fn marginal_ranking_loss( x_first: &[f64], x_second: &[f64], margin: f64, @@ -63,7 +63,7 @@ mod tests { #[test] fn $name() { let (x_first, x_second, margin, y_true, expected) = $inputs; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), expected); + assert_eq!(marginal_ranking_loss(&x_first, &x_second, margin, y_true), expected); } )* } @@ -85,7 +85,7 @@ mod tests { #[test] fn $name() { let (x_first, x_second, margin, y_true, expected) = $inputs; - assert_eq!(mrg_ranking_loss(&x_first, &x_second, margin, y_true), Ok(expected)); + assert_eq!(marginal_ranking_loss(&x_first, &x_second, margin, y_true), Ok(expected)); } )* } From 7dd2fbf8659d71f04e9d5b5a2745fbdf3e542130 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 18 Jul 2024 11:28:45 +0200 Subject: [PATCH 15/21] Feat: added 1 more possible error message --- src/machine_learning/loss_function/marginal_ranking.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 234a8c989c2..1b773d9883b 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -32,7 +32,7 @@ pub fn marginal_ranking_loss( return Err(MarginalRankingLossError::EmptyInputs); } if margin < 0.0 { - return Err(MarginalRankingLossError::InvalidValues); + return Err(MarginalRankingLossError::NegativeMargin); } if y_true != 1.0 && y_true != -1.0 { return Err(MarginalRankingLossError::InvalidValues); @@ -51,6 +51,7 @@ pub enum MarginalRankingLossError { InputsHaveDifferentLength, EmptyInputs, InvalidValues, + NegativeMargin, } #[cfg(test)] From 455fa8a1059cdabc6b00f8d202f2397a57c9ac32 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 18 Jul 2024 11:30:25 +0200 Subject: [PATCH 16/21] Test: added symmetric error handling --- src/machine_learning/loss_function/marginal_ranking.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 1b773d9883b..b432b6b041a 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -63,8 +63,9 @@ mod tests { $( #[test] fn $name() { - let (x_first, x_second, margin, y_true, expected) = $inputs; - assert_eq!(marginal_ranking_loss(&x_first, &x_second, margin, y_true), expected); + let (vec_a, vec_b, margin, y_true, expected) = $inputs; + assert_eq!(marginal_ranking_loss(&vec_a, &vec_b, margin, y_true), expected); + assert_eq!(marginal_ranking_loss(&vec_b, &vec_a, margin, y_true), expected); } )* } From 94b9e8124966bc0dd7bcf5a359b55e38a9dc6f0d Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 18 Jul 2024 11:36:25 +0200 Subject: [PATCH 17/21] Refactoring: added more rust-like syntaxis --- .../loss_function/marginal_ranking.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index b432b6b041a..07094ecf854 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -38,11 +38,11 @@ pub fn marginal_ranking_loss( return Err(MarginalRankingLossError::InvalidValues); } - let mut total_loss: f64 = 0.0; - for (f, s) in x_first.iter().zip(x_second.iter()) { - let loss: f64 = (margin - y_true * (f - s)).max(0.0); - total_loss += loss; - } + let total_loss: f64 = x_first + .iter() + .zip(x_second.iter()) + .map(|(f, s)| (margin - y_true * (f - s)).max(0.0)) + .sum(); Ok(total_loss / (x_first.len() as f64)) } @@ -76,7 +76,7 @@ mod tests { invalid_length1: (vec![1.0, 2.0], vec![2.0, 3.0, 4.0], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), invalid_length2: (vec![], vec![1.0, 2.0, 3.0], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), invalid_length3: (vec![1.0, 2.0, 3.0], vec![], 1.0, 1.0, Err(MarginalRankingLossError::InputsHaveDifferentLength)), - invalid_values: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], -1.0, 1.0, Err(MarginalRankingLossError::InvalidValues)), + invalid_values: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], -1.0, 1.0, Err(MarginalRankingLossError::NegativeMargin)), invalid_y_true: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, 2.0, Err(MarginalRankingLossError::InvalidValues)), empty_inputs: (vec![], vec![], 1.0, 1.0, Err(MarginalRankingLossError::EmptyInputs)), } From af0efde821c0556b2ce0ac642548a1d22e27d85b Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Thu, 18 Jul 2024 11:37:38 +0200 Subject: [PATCH 18/21] Feat: fixed with the correct export --- src/machine_learning/loss_function/mod.rs | 2 +- src/machine_learning/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/machine_learning/loss_function/mod.rs b/src/machine_learning/loss_function/mod.rs index da28315d066..1ef8d76ae85 100644 --- a/src/machine_learning/loss_function/mod.rs +++ b/src/machine_learning/loss_function/mod.rs @@ -8,6 +8,6 @@ mod mean_squared_error_loss; pub use self::hinge_loss::hng_loss; pub use self::huber_loss::huber_loss; pub use self::kl_divergence_loss::kld_loss; -pub use self::marginal_ranking::mrg_ranking_loss; +pub use self::marginal_ranking::marginal_ranking_loss; pub use self::mean_absolute_error_loss::mae_loss; pub use self::mean_squared_error_loss::mse_loss; diff --git a/src/machine_learning/mod.rs b/src/machine_learning/mod.rs index 8d12c3ebca0..91bbb6d6777 100644 --- a/src/machine_learning/mod.rs +++ b/src/machine_learning/mod.rs @@ -11,7 +11,7 @@ pub use self::loss_function::hng_loss; pub use self::loss_function::huber_loss; pub use self::loss_function::kld_loss; pub use self::loss_function::mae_loss; -pub use self::loss_function::mrg_ranking_loss; +pub use self::loss_function::marginal_ranking_loss; pub use self::loss_function::mse_loss; pub use self::optimization::gradient_descent; pub use self::optimization::Adam; From b9f26cc47309409f092c7948dfa3429b8b58f919 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Mon, 29 Jul 2024 14:28:38 +0200 Subject: [PATCH 19/21] Feat: added suggested check_input function --- .../loss_function/marginal_ranking.rs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/marginal_ranking.rs index 07094ecf854..86e58eddfed 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/marginal_ranking.rs @@ -25,6 +25,22 @@ pub fn marginal_ranking_loss( margin: f64, y_true: f64, ) -> Result { + check_input(x_first, x_second, margin, y_true)?; + + let total_loss: f64 = x_first + .iter() + .zip(x_second.iter()) + .map(|(f, s)| (margin - y_true * (f - s)).max(0.0)) + .sum(); + Ok(total_loss / (x_first.len() as f64)) +} + +fn check_input( + x_first: &[f64], + x_second: &[f64], + margin: f64, + y_true: f64, +) -> Result<(), MarginalRankingLossError> { if x_first.len() != x_second.len() { return Err(MarginalRankingLossError::InputsHaveDifferentLength); } @@ -38,12 +54,7 @@ pub fn marginal_ranking_loss( return Err(MarginalRankingLossError::InvalidValues); } - let total_loss: f64 = x_first - .iter() - .zip(x_second.iter()) - .map(|(f, s)| (margin - y_true * (f - s)).max(0.0)) - .sum(); - Ok(total_loss / (x_first.len() as f64)) + Ok(()) } #[derive(Debug, PartialEq, Eq)] From c7cb14b8dc405ae411593b0f92973105afbec918 Mon Sep 17 00:00:00 2001 From: Javier Kauer Date: Tue, 30 Jul 2024 15:26:14 +0200 Subject: [PATCH 20/21] Refactoring: changed the name to margin ranking loss --- ...ranking.rs => average_margin_ranking_loss.rs} | 16 ++++++++-------- src/machine_learning/loss_function/mod.rs | 4 ++-- src/machine_learning/mod.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/machine_learning/loss_function/{marginal_ranking.rs => average_margin_ranking_loss.rs} (85%) diff --git a/src/machine_learning/loss_function/marginal_ranking.rs b/src/machine_learning/loss_function/average_margin_ranking_loss.rs similarity index 85% rename from src/machine_learning/loss_function/marginal_ranking.rs rename to src/machine_learning/loss_function/average_margin_ranking_loss.rs index 86e58eddfed..505bf2a94a7 100644 --- a/src/machine_learning/loss_function/marginal_ranking.rs +++ b/src/machine_learning/loss_function/average_margin_ranking_loss.rs @@ -1,12 +1,12 @@ /// Marginal Ranking /// -/// The 'marginal_ranking_loss' function calculates the Marginal Ranking loss, which is a +/// The 'average_margin_ranking_loss' function calculates the Margin Ranking loss, which is a /// loss function used for ranking problems in machine learning. /// /// ## Formula /// /// For a pair of values `x_first` and `x_second`, `margin`, and `y_true`, -/// the Marginal Ranking loss is calculated as: +/// the Margin Ranking loss is calculated as: /// /// - loss = `max(0, -y_true * (x_first - x_second) + margin)`. /// @@ -19,7 +19,7 @@ /// https://vinija.ai/concepts/loss/#pairwise-ranking-loss /// -pub fn marginal_ranking_loss( +pub fn average_margin_ranking_loss( x_first: &[f64], x_second: &[f64], margin: f64, @@ -75,8 +75,8 @@ mod tests { #[test] fn $name() { let (vec_a, vec_b, margin, y_true, expected) = $inputs; - assert_eq!(marginal_ranking_loss(&vec_a, &vec_b, margin, y_true), expected); - assert_eq!(marginal_ranking_loss(&vec_b, &vec_a, margin, y_true), expected); + assert_eq!(average_margin_ranking_loss(&vec_a, &vec_b, margin, y_true), expected); + assert_eq!(average_margin_ranking_loss(&vec_b, &vec_a, margin, y_true), expected); } )* } @@ -92,19 +92,19 @@ mod tests { empty_inputs: (vec![], vec![], 1.0, 1.0, Err(MarginalRankingLossError::EmptyInputs)), } - macro_rules! test_marginal_ranking_loss { + macro_rules! test_average_margin_ranking_loss { ($($name:ident: $inputs:expr,)*) => { $( #[test] fn $name() { let (x_first, x_second, margin, y_true, expected) = $inputs; - assert_eq!(marginal_ranking_loss(&x_first, &x_second, margin, y_true), Ok(expected)); + assert_eq!(average_margin_ranking_loss(&x_first, &x_second, margin, y_true), Ok(expected)); } )* } } - test_marginal_ranking_loss! { + test_average_margin_ranking_loss! { set_0: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, -1.0, 0.0), set_1: (vec![1.0, 2.0, 3.0], vec![2.0, 3.0, 4.0], 1.0, 1.0, 2.0), set_2: (vec![1.0, 2.0, 3.0], vec![1.0, 2.0, 3.0], 0.0, 1.0, 0.0), diff --git a/src/machine_learning/loss_function/mod.rs b/src/machine_learning/loss_function/mod.rs index 1ef8d76ae85..3f7ce25798e 100644 --- a/src/machine_learning/loss_function/mod.rs +++ b/src/machine_learning/loss_function/mod.rs @@ -1,13 +1,13 @@ +mod average_margin_ranking_loss; mod hinge_loss; mod huber_loss; mod kl_divergence_loss; -mod marginal_ranking; mod mean_absolute_error_loss; mod mean_squared_error_loss; +pub use self::average_margin_ranking_loss::average_margin_ranking_loss; pub use self::hinge_loss::hng_loss; pub use self::huber_loss::huber_loss; pub use self::kl_divergence_loss::kld_loss; -pub use self::marginal_ranking::marginal_ranking_loss; pub use self::mean_absolute_error_loss::mae_loss; pub use self::mean_squared_error_loss::mse_loss; diff --git a/src/machine_learning/mod.rs b/src/machine_learning/mod.rs index 91bbb6d6777..0b3a4d34746 100644 --- a/src/machine_learning/mod.rs +++ b/src/machine_learning/mod.rs @@ -7,11 +7,11 @@ mod optimization; pub use self::cholesky::cholesky; pub use self::k_means::k_means; pub use self::linear_regression::linear_regression; +pub use self::loss_function::average_margin_ranking_loss; pub use self::loss_function::hng_loss; pub use self::loss_function::huber_loss; pub use self::loss_function::kld_loss; pub use self::loss_function::mae_loss; -pub use self::loss_function::marginal_ranking_loss; pub use self::loss_function::mse_loss; pub use self::optimization::gradient_descent; pub use self::optimization::Adam; From 90f1be45e38d0681b44d6591ff427ad0bea75741 Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:28:41 +0200 Subject: [PATCH 21/21] docs: update dead link --- DIRECTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4db1d4032df..c24ddd86ef4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -155,7 +155,7 @@ * [Hinge Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/hinge_loss.rs) * [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/huber_loss.rs) * [Kl Divergence Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/kl_divergence_loss.rs) - * [Marginal Ranking Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/marginal_ranking.rs) + * [Marginal Ranking Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/average_margin_ranking_loss.rs) * [Mean Absolute Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_absolute_error_loss.rs) * [Mean Squared Error Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/mean_squared_error_loss.rs) * Optimization