Merge branch 'development' into ridge

This commit is contained in:
VolodymyrOrlov
2020-11-11 16:12:34 -08:00
committed by GitHub
59 changed files with 793 additions and 499 deletions
+17
View File
@@ -1,5 +1,11 @@
version: 2.1 version: 2.1
workflows:
version: 2.1
build:
jobs:
- build
- clippy
jobs: jobs:
build: build:
docker: docker:
@@ -24,3 +30,14 @@ jobs:
paths: paths:
- "~/.cargo" - "~/.cargo"
- "./target" - "./target"
clippy:
docker:
- image: circleci/rust:latest
steps:
- checkout
- run:
name: Install cargo clippy
command: rustup component add clippy
- run:
name: Run cargo clippy
command: cargo clippy --all-features -- -Drust-2018-idioms -Dwarnings
+12 -14
View File
@@ -50,8 +50,8 @@ impl<T: RealNumber> BBDTree<T> {
} }
let mut tree = BBDTree { let mut tree = BBDTree {
nodes: nodes, nodes,
index: index, index,
root: 0, root: 0,
}; };
@@ -113,7 +113,7 @@ impl<T: RealNumber> BBDTree<T> {
} }
} }
if !self.nodes[node].lower.is_none() { if self.nodes[node].lower.is_some() {
let mut new_candidates = vec![0; k]; let mut new_candidates = vec![0; k];
let mut newk = 0; let mut newk = 0;
@@ -134,7 +134,7 @@ impl<T: RealNumber> BBDTree<T> {
return self.filter( return self.filter(
self.nodes[node].lower.unwrap(), self.nodes[node].lower.unwrap(),
centroids, centroids,
&mut new_candidates, &new_candidates,
newk, newk,
sums, sums,
counts, counts,
@@ -142,7 +142,7 @@ impl<T: RealNumber> BBDTree<T> {
) + self.filter( ) + self.filter(
self.nodes[node].upper.unwrap(), self.nodes[node].upper.unwrap(),
centroids, centroids,
&mut new_candidates, &new_candidates,
newk, newk,
sums, sums,
counts, counts,
@@ -152,7 +152,7 @@ impl<T: RealNumber> BBDTree<T> {
} }
for i in 0..d { for i in 0..d {
sums[closest][i] = sums[closest][i] + self.nodes[node].sum[i]; sums[closest][i] += self.nodes[node].sum[i];
} }
counts[closest] += self.nodes[node].count; counts[closest] += self.nodes[node].count;
@@ -184,11 +184,11 @@ impl<T: RealNumber> BBDTree<T> {
let mut rhs = T::zero(); let mut rhs = T::zero();
for i in 0..d { for i in 0..d {
let diff = test[i] - best[i]; let diff = test[i] - best[i];
lhs = lhs + diff * diff; lhs += diff * diff;
if diff > T::zero() { if diff > T::zero() {
rhs = rhs + (center[i] + radius[i] - best[i]) * diff; rhs += (center[i] + radius[i] - best[i]) * diff;
} else { } else {
rhs = rhs + (center[i] - radius[i] - best[i]) * diff; rhs += (center[i] - radius[i] - best[i]) * diff;
} }
} }
@@ -244,7 +244,7 @@ impl<T: RealNumber> BBDTree<T> {
if end > begin + 1 { if end > begin + 1 {
let len = end - begin; let len = end - begin;
for i in 0..d { for i in 0..d {
node.sum[i] = node.sum[i] * T::from(len).unwrap(); node.sum[i] *= T::from(len).unwrap();
} }
} }
@@ -261,9 +261,7 @@ impl<T: RealNumber> BBDTree<T> {
let mut i2_good = data.get(self.index[i2], split_index) >= split_cutoff; let mut i2_good = data.get(self.index[i2], split_index) >= split_cutoff;
if !i1_good && !i2_good { if !i1_good && !i2_good {
let temp = self.index[i1]; self.index.swap(i1, i2);
self.index[i1] = self.index[i2];
self.index[i2] = temp;
i1_good = true; i1_good = true;
i2_good = true; i2_good = true;
} }
@@ -302,7 +300,7 @@ impl<T: RealNumber> BBDTree<T> {
let mut scatter = T::zero(); let mut scatter = T::zero();
for i in 0..d { for i in 0..d {
let x = (node.sum[i] / T::from(node.count).unwrap()) - center[i]; let x = (node.sum[i] / T::from(node.count).unwrap()) - center[i];
scatter = scatter + x * x; scatter += x * x;
} }
node.cost + T::from(node.count).unwrap() * scatter node.cost + T::from(node.count).unwrap() * scatter
} }
+15 -14
View File
@@ -51,7 +51,7 @@ impl<T, F: RealNumber, D: Distance<T, F>> PartialEq for CoverTree<T, F, D> {
return false; return false;
} }
} }
return true; true
} }
} }
@@ -84,11 +84,11 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
scale: 0, scale: 0,
}; };
let mut tree = CoverTree { let mut tree = CoverTree {
base: base, base,
inv_log_base: F::one() / base.ln(), inv_log_base: F::one() / base.ln(),
distance: distance, distance,
root: root, root,
data: data, data,
identical_excluded: false, identical_excluded: false,
}; };
@@ -101,7 +101,7 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
/// * `p` - look for k nearest points to `p` /// * `p` - look for k nearest points to `p`
/// * `k` - the number of nearest neighbors to return /// * `k` - the number of nearest neighbors to return
pub fn find(&self, p: &T, k: usize) -> Result<Vec<(usize, F, &T)>, Failed> { pub fn find(&self, p: &T, k: usize) -> Result<Vec<(usize, F, &T)>, Failed> {
if k <= 0 { if k == 0 {
return Err(Failed::because(FailedError::FindFailed, "k should be > 0")); return Err(Failed::because(FailedError::FindFailed, "k should be > 0"));
} }
@@ -147,10 +147,11 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
*heap.peek() *heap.peek()
}; };
if d <= (upper_bound + child.max_dist) { if d <= (upper_bound + child.max_dist) {
if c > 0 && d < upper_bound { if c > 0
if !self.identical_excluded || self.get_data_value(child.idx) != p { && d < upper_bound
heap.add(d); && (!self.identical_excluded || self.get_data_value(child.idx) != p)
} {
heap.add(d);
} }
if !child.children.is_empty() { if !child.children.is_empty() {
@@ -234,7 +235,7 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
fn new_leaf(&self, idx: usize) -> Node<F> { fn new_leaf(&self, idx: usize) -> Node<F> {
Node { Node {
idx: idx, idx,
max_dist: F::zero(), max_dist: F::zero(),
parent_dist: F::zero(), parent_dist: F::zero(),
children: Vec::new(), children: Vec::new(),
@@ -298,7 +299,7 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
idx: p, idx: p,
max_dist: F::zero(), max_dist: F::zero(),
parent_dist: F::zero(), parent_dist: F::zero(),
children: children, children,
scale: 100, scale: 100,
} }
} else { } else {
@@ -368,7 +369,7 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
idx: p, idx: p,
max_dist: self.max(consumed_set), max_dist: self.max(consumed_set),
parent_dist: F::zero(), parent_dist: F::zero(),
children: children, children,
scale: (top_scale - max_scale), scale: (top_scale - max_scale),
} }
} }
@@ -442,7 +443,7 @@ impl<T: Debug + PartialEq, F: RealNumber, D: Distance<T, F>> CoverTree<T, F, D>
max = n.dist[n.dist.len() - 1]; max = n.dist[n.dist.len() - 1];
} }
} }
return max; max
} }
} }
+5 -5
View File
@@ -44,8 +44,8 @@ impl<T, F: RealNumber, D: Distance<T, F>> LinearKNNSearch<T, F, D> {
/// * `distance` - distance metric to use for searching. This function should extend [`Distance`](../../../math/distance/index.html) interface. /// * `distance` - distance metric to use for searching. This function should extend [`Distance`](../../../math/distance/index.html) interface.
pub fn new(data: Vec<T>, distance: D) -> Result<LinearKNNSearch<T, F, D>, Failed> { pub fn new(data: Vec<T>, distance: D) -> Result<LinearKNNSearch<T, F, D>, Failed> {
Ok(LinearKNNSearch { Ok(LinearKNNSearch {
data: data, data,
distance: distance, distance,
f: PhantomData, f: PhantomData,
}) })
} }
@@ -157,7 +157,7 @@ mod tests {
.iter() .iter()
.map(|v| v.0) .map(|v| v.0)
.collect(); .collect();
found_idxs1.sort(); found_idxs1.sort_unstable();
assert_eq!(vec!(0, 1, 2), found_idxs1); assert_eq!(vec!(0, 1, 2), found_idxs1);
@@ -167,7 +167,7 @@ mod tests {
.iter() .iter()
.map(|v| *v.2) .map(|v| *v.2)
.collect(); .collect();
found_idxs1.sort(); found_idxs1.sort_unstable();
assert_eq!(vec!(2, 3, 4, 5, 6, 7, 8), found_idxs1); assert_eq!(vec!(2, 3, 4, 5, 6, 7, 8), found_idxs1);
@@ -187,7 +187,7 @@ mod tests {
.iter() .iter()
.map(|v| v.0) .map(|v| v.0)
.collect(); .collect();
found_idxs2.sort(); found_idxs2.sort_unstable();
assert_eq!(vec!(1, 2, 3), found_idxs2); assert_eq!(vec!(1, 2, 3), found_idxs2);
} }
+2 -2
View File
@@ -66,10 +66,10 @@ impl KNNAlgorithmName {
) -> Result<KNNAlgorithm<T, D>, Failed> { ) -> Result<KNNAlgorithm<T, D>, Failed> {
match *self { match *self {
KNNAlgorithmName::LinearSearch => { KNNAlgorithmName::LinearSearch => {
LinearKNNSearch::new(data, distance).map(|a| KNNAlgorithm::LinearSearch(a)) LinearKNNSearch::new(data, distance).map(KNNAlgorithm::LinearSearch)
} }
KNNAlgorithmName::CoverTree => { KNNAlgorithmName::CoverTree => {
CoverTree::new(data, distance).map(|a| KNNAlgorithm::CoverTree(a)) CoverTree::new(data, distance).map(KNNAlgorithm::CoverTree)
} }
} }
} }
+4 -4
View File
@@ -15,7 +15,7 @@ pub struct HeapSelection<T: PartialOrd + Debug> {
impl<'a, T: PartialOrd + Debug> HeapSelection<T> { impl<'a, T: PartialOrd + Debug> HeapSelection<T> {
pub fn with_capacity(k: usize) -> HeapSelection<T> { pub fn with_capacity(k: usize) -> HeapSelection<T> {
HeapSelection { HeapSelection {
k: k, k,
n: 0, n: 0,
sorted: false, sorted: false,
heap: Vec::new(), heap: Vec::new(),
@@ -51,7 +51,7 @@ impl<'a, T: PartialOrd + Debug> HeapSelection<T> {
pub fn peek(&self) -> &T { pub fn peek(&self) -> &T {
if self.sorted { if self.sorted {
return &self.heap[0]; &self.heap[0]
} else { } else {
&self &self
.heap .heap
@@ -62,11 +62,11 @@ impl<'a, T: PartialOrd + Debug> HeapSelection<T> {
} }
pub fn peek_mut(&mut self) -> &mut T { pub fn peek_mut(&mut self) -> &mut T {
return &mut self.heap[0]; &mut self.heap[0]
} }
pub fn get(self) -> Vec<T> { pub fn get(self) -> Vec<T> {
return self.heap; self.heap
} }
fn sift_down(&mut self, k: usize, n: usize) { fn sift_down(&mut self, k: usize, n: usize) {
+2 -4
View File
@@ -29,8 +29,6 @@
//! * ["A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise", Ester M., Kriegel HP., Sander J., Xu X.](http://faculty.marshall.usc.edu/gareth-james/ISL/) //! * ["A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise", Ester M., Kriegel HP., Sander J., Xu X.](http://faculty.marshall.usc.edu/gareth-james/ISL/)
//! * ["Density-Based Clustering in Spatial Databases: The Algorithm GDBSCAN and its Applications", Sander J., Ester M., Kriegel HP., Xu X.](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.63.1629&rep=rep1&type=pdf) //! * ["Density-Based Clustering in Spatial Databases: The Algorithm GDBSCAN and its Applications", Sander J., Ester M., Kriegel HP., Xu X.](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.63.1629&rep=rep1&type=pdf)
extern crate rand;
use std::fmt::Debug; use std::fmt::Debug;
use std::iter::Sum; use std::iter::Sum;
@@ -93,11 +91,11 @@ impl<T: RealNumber + Sum, D: Distance<Vec<T>, T>> DBSCAN<T, D> {
parameters: DBSCANParameters<T>, parameters: DBSCANParameters<T>,
) -> Result<DBSCAN<T, D>, Failed> { ) -> Result<DBSCAN<T, D>, Failed> {
if parameters.min_samples < 1 { if parameters.min_samples < 1 {
return Err(Failed::fit(&format!("Invalid minPts"))); return Err(Failed::fit(&"Invalid minPts".to_string()));
} }
if parameters.eps <= T::zero() { if parameters.eps <= T::zero() {
return Err(Failed::fit(&format!("Invalid radius: "))); return Err(Failed::fit(&"Invalid radius: ".to_string()));
} }
let mut k = 0; let mut k = 0;
+10 -12
View File
@@ -52,8 +52,6 @@
//! * ["An Introduction to Statistical Learning", James G., Witten D., Hastie T., Tibshirani R., 10.3.1 K-Means Clustering](http://faculty.marshall.usc.edu/gareth-james/ISL/) //! * ["An Introduction to Statistical Learning", James G., Witten D., Hastie T., Tibshirani R., 10.3.1 K-Means Clustering](http://faculty.marshall.usc.edu/gareth-james/ISL/)
//! * ["k-means++: The Advantages of Careful Seeding", Arthur D., Vassilvitskii S.](http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf) //! * ["k-means++: The Advantages of Careful Seeding", Arthur D., Vassilvitskii S.](http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf)
extern crate rand;
use rand::Rng; use rand::Rng;
use std::fmt::Debug; use std::fmt::Debug;
use std::iter::Sum; use std::iter::Sum;
@@ -129,7 +127,7 @@ impl<T: RealNumber + Sum> KMeans<T> {
return Err(Failed::fit(&format!("invalid number of clusters: {}", k))); return Err(Failed::fit(&format!("invalid number of clusters: {}", k)));
} }
if parameters.max_iter <= 0 { if parameters.max_iter == 0 {
return Err(Failed::fit(&format!( return Err(Failed::fit(&format!(
"invalid maximum number of iterations: {}", "invalid maximum number of iterations: {}",
parameters.max_iter parameters.max_iter
@@ -149,13 +147,13 @@ impl<T: RealNumber + Sum> KMeans<T> {
for i in 0..n { for i in 0..n {
for j in 0..d { for j in 0..d {
centroids[y[i]][j] = centroids[y[i]][j] + data.get(i, j); centroids[y[i]][j] += data.get(i, j);
} }
} }
for i in 0..k { for i in 0..k {
for j in 0..d { for j in 0..d {
centroids[i][j] = centroids[i][j] / T::from(size[i]).unwrap(); centroids[i][j] /= T::from(size[i]).unwrap();
} }
} }
@@ -178,11 +176,11 @@ impl<T: RealNumber + Sum> KMeans<T> {
} }
Ok(KMeans { Ok(KMeans {
k: k, k,
y: y, y,
size: size, size,
distortion: distortion, distortion,
centroids: centroids, centroids,
}) })
} }
@@ -235,13 +233,13 @@ impl<T: RealNumber + Sum> KMeans<T> {
let mut sum: T = T::zero(); let mut sum: T = T::zero();
for i in d.iter() { for i in d.iter() {
sum = sum + *i; sum += *i;
} }
let cutoff = T::from(rng.gen::<f64>()).unwrap() * sum; let cutoff = T::from(rng.gen::<f64>()).unwrap() * sum;
let mut cost = T::zero(); let mut cost = T::zero();
let mut index = 0; let mut index = 0;
while index < n { while index < n {
cost = cost + d[index]; cost += d[index];
if cost >= cutoff { if cost >= cutoff {
break; break;
} }
+2 -2
View File
@@ -38,8 +38,8 @@ pub fn load_dataset() -> Dataset<f32, f32> {
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: vec![ feature_names: vec![
"CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B", "CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B",
"LSTAT", "LSTAT",
+2 -2
View File
@@ -40,8 +40,8 @@ pub fn load_dataset() -> Dataset<f32, f32> {
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: vec![ feature_names: vec![
"mean radius", "mean texture", "mean perimeter", "mean area", "mean radius", "mean texture", "mean perimeter", "mean area",
"mean smoothness", "mean compactness", "mean concavity", "mean smoothness", "mean compactness", "mean concavity",
+2 -2
View File
@@ -33,8 +33,8 @@ pub fn load_dataset() -> Dataset<f32, f32> {
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: vec![ feature_names: vec![
"Age", "Sex", "BMI", "BP", "S1", "S2", "S3", "S4", "S5", "S6", "Age", "Sex", "BMI", "BP", "S1", "S2", "S3", "S4", "S5", "S6",
] ]
+2 -2
View File
@@ -23,8 +23,8 @@ pub fn load_dataset() -> Dataset<f32, f32> {
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: vec![ feature_names: vec![
"sepal length (cm)", "sepal length (cm)",
"sepal width (cm)", "sepal width (cm)",
+4 -4
View File
@@ -39,8 +39,8 @@ pub fn make_blobs(
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: (0..num_features).map(|n| n.to_string()).collect(), feature_names: (0..num_features).map(|n| n.to_string()).collect(),
target_names: vec!["label".to_string()], target_names: vec!["label".to_string()],
description: "Isotropic Gaussian blobs".to_string(), description: "Isotropic Gaussian blobs".to_string(),
@@ -49,7 +49,7 @@ pub fn make_blobs(
/// Make a large circle containing a smaller circle in 2d. /// Make a large circle containing a smaller circle in 2d.
pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset<f32, f32> { pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset<f32, f32> {
if factor >= 1.0 || factor < 0.0 { if !(0.0..1.0).contains(&factor) {
panic!("'factor' has to be between 0 and 1."); panic!("'factor' has to be between 0 and 1.");
} }
@@ -82,7 +82,7 @@ pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset<f32,
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: 2, num_features: 2,
feature_names: (0..2).map(|n| n.to_string()).collect(), feature_names: (0..2).map(|n| n.to_string()).collect(),
target_names: vec!["label".to_string()], target_names: vec!["label".to_string()],
+2 -2
View File
@@ -28,8 +28,8 @@ pub fn load_dataset() -> Dataset<f32, f32> {
Dataset { Dataset {
data: x, data: x,
target: y, target: y,
num_samples: num_samples, num_samples,
num_features: num_features, num_features,
feature_names: vec![ feature_names: vec![
"sepal length (cm)", "sepal length (cm)",
"sepal width (cm)", "sepal width (cm)",
+4 -4
View File
@@ -56,19 +56,19 @@ pub(crate) fn serialize_data<X: RealNumber, Y: RealNumber>(
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
match File::create(filename) { match File::create(filename) {
Ok(mut file) => { Ok(mut file) => {
file.write(&dataset.num_features.to_le_bytes())?; file.write_all(&dataset.num_features.to_le_bytes())?;
file.write(&dataset.num_samples.to_le_bytes())?; file.write_all(&dataset.num_samples.to_le_bytes())?;
let x: Vec<u8> = dataset let x: Vec<u8> = dataset
.data .data
.iter() .iter()
.map(|v| *v) .copied()
.flat_map(|f| f.to_f32_bits().to_le_bytes().to_vec().into_iter()) .flat_map(|f| f.to_f32_bits().to_le_bytes().to_vec().into_iter())
.collect(); .collect();
file.write_all(&x)?; file.write_all(&x)?;
let y: Vec<u8> = dataset let y: Vec<u8> = dataset
.target .target
.iter() .iter()
.map(|v| *v) .copied()
.flat_map(|f| f.to_f32_bits().to_le_bytes().to_vec().into_iter()) .flat_map(|f| f.to_f32_bits().to_le_bytes().to_vec().into_iter())
.collect(); .collect();
file.write_all(&y)?; file.write_all(&y)?;
+7 -7
View File
@@ -68,14 +68,14 @@ impl<T: RealNumber, M: Matrix<T>> PartialEq for PCA<T, M> {
if self.eigenvectors != other.eigenvectors if self.eigenvectors != other.eigenvectors
|| self.eigenvalues.len() != other.eigenvalues.len() || self.eigenvalues.len() != other.eigenvalues.len()
{ {
return false; false
} else { } else {
for i in 0..self.eigenvalues.len() { for i in 0..self.eigenvalues.len() {
if (self.eigenvalues[i] - other.eigenvalues[i]).abs() > T::epsilon() { if (self.eigenvalues[i] - other.eigenvalues[i]).abs() > T::epsilon() {
return false; return false;
} }
} }
return true; true
} }
} }
} }
@@ -190,16 +190,16 @@ impl<T: RealNumber, M: Matrix<T>> PCA<T, M> {
let mut pmu = vec![T::zero(); n_components]; let mut pmu = vec![T::zero(); n_components];
for k in 0..n { for k in 0..n {
for i in 0..n_components { for i in 0..n_components {
pmu[i] = pmu[i] + projection.get(i, k) * mu[k]; pmu[i] += projection.get(i, k) * mu[k];
} }
} }
Ok(PCA { Ok(PCA {
eigenvectors: eigenvectors, eigenvectors,
eigenvalues: eigenvalues, eigenvalues,
projection: projection.transpose(), projection: projection.transpose(),
mu: mu, mu,
pmu: pmu, pmu,
}) })
} }
+7 -9
View File
@@ -45,8 +45,6 @@
//! //!
//! <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> //! <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
//! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> //! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
extern crate rand;
use std::default::Default; use std::default::Default;
use std::fmt::Debug; use std::fmt::Debug;
@@ -89,7 +87,7 @@ pub struct RandomForestClassifier<T: RealNumber> {
impl<T: RealNumber> PartialEq for RandomForestClassifier<T> { impl<T: RealNumber> PartialEq for RandomForestClassifier<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.classes.len() != other.classes.len() || self.trees.len() != other.trees.len() { if self.classes.len() != other.classes.len() || self.trees.len() != other.trees.len() {
return false; false
} else { } else {
for i in 0..self.classes.len() { for i in 0..self.classes.len() {
if (self.classes[i] - other.classes[i]).abs() > T::epsilon() { if (self.classes[i] - other.classes[i]).abs() > T::epsilon() {
@@ -139,13 +137,13 @@ impl<T: RealNumber> RandomForestClassifier<T> {
yi[i] = classes.iter().position(|c| yc == *c).unwrap(); yi[i] = classes.iter().position(|c| yc == *c).unwrap();
} }
let mtry = parameters.m.unwrap_or( let mtry = parameters.m.unwrap_or_else(|| {
(T::from(num_attributes).unwrap()) (T::from(num_attributes).unwrap())
.sqrt() .sqrt()
.floor() .floor()
.to_usize() .to_usize()
.unwrap(), .unwrap()
); });
let classes = y_m.unique(); let classes = y_m.unique();
let k = classes.len(); let k = classes.len();
@@ -164,8 +162,8 @@ impl<T: RealNumber> RandomForestClassifier<T> {
} }
Ok(RandomForestClassifier { Ok(RandomForestClassifier {
parameters: parameters, parameters,
trees: trees, trees,
classes, classes,
}) })
} }
@@ -191,7 +189,7 @@ impl<T: RealNumber> RandomForestClassifier<T> {
result[tree.predict_for_row(x, row)] += 1; result[tree.predict_for_row(x, row)] += 1;
} }
return which_max(&result); which_max(&result)
} }
fn sample_with_replacement(y: &Vec<usize>, num_classes: usize) -> Vec<usize> { fn sample_with_replacement(y: &Vec<usize>, num_classes: usize) -> Vec<usize> {
+3 -7
View File
@@ -42,7 +42,6 @@
//! //!
//! <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> //! <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
//! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> //! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
extern crate rand;
use std::default::Default; use std::default::Default;
use std::fmt::Debug; use std::fmt::Debug;
@@ -95,7 +94,7 @@ impl Default for RandomForestRegressorParameters {
impl<T: RealNumber> PartialEq for RandomForestRegressor<T> { impl<T: RealNumber> PartialEq for RandomForestRegressor<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.trees.len() != other.trees.len() { if self.trees.len() != other.trees.len() {
return false; false
} else { } else {
for i in 0..self.trees.len() { for i in 0..self.trees.len() {
if self.trees[i] != other.trees[i] { if self.trees[i] != other.trees[i] {
@@ -135,10 +134,7 @@ impl<T: RealNumber> RandomForestRegressor<T> {
trees.push(tree); trees.push(tree);
} }
Ok(RandomForestRegressor { Ok(RandomForestRegressor { parameters, trees })
parameters: parameters,
trees: trees,
})
} }
/// Predict class for `x` /// Predict class for `x`
@@ -161,7 +157,7 @@ impl<T: RealNumber> RandomForestRegressor<T> {
let mut result = T::zero(); let mut result = T::zero();
for tree in self.trees.iter() { for tree in self.trees.iter() {
result = result + tree.predict_for_row(x, row); result += tree.predict_for_row(x, row);
} }
result / T::from(n_trees).unwrap() result / T::from(n_trees).unwrap()
+3 -3
View File
@@ -61,7 +61,7 @@ impl Failed {
/// new instance of `err` /// new instance of `err`
pub fn because(err: FailedError, msg: &str) -> Self { pub fn because(err: FailedError, msg: &str) -> Self {
Failed { Failed {
err: err, err,
msg: msg.to_string(), msg: msg.to_string(),
} }
} }
@@ -82,7 +82,7 @@ impl PartialEq for Failed {
} }
impl fmt::Display for FailedError { impl fmt::Display for FailedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let failed_err_str = match self { let failed_err_str = match self {
FailedError::FitFailed => "Fit failed", FailedError::FitFailed => "Fit failed",
FailedError::PredictFailed => "Predict failed", FailedError::PredictFailed => "Predict failed",
@@ -96,7 +96,7 @@ impl fmt::Display for FailedError {
} }
impl fmt::Display for Failed { impl fmt::Display for Failed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.err, self.msg) write!(f, "{}: {}", self.err, self.msg)
} }
} }
+9
View File
@@ -1,3 +1,10 @@
#![allow(
clippy::needless_range_loop,
clippy::ptr_arg,
clippy::type_complexity,
clippy::too_many_arguments,
clippy::many_single_char_names
)]
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(missing_doc_code_examples)] #![warn(missing_doc_code_examples)]
@@ -85,6 +92,8 @@ pub mod math;
/// Functions for assessing prediction error. /// Functions for assessing prediction error.
pub mod metrics; pub mod metrics;
pub mod model_selection; pub mod model_selection;
/// Supervised learning algorithms based on applying the Bayes theorem with the independence assumptions between predictors
pub mod naive_bayes;
/// Supervised neighbors-based learning methods /// Supervised neighbors-based learning methods
pub mod neighbors; pub mod neighbors;
pub(crate) mod optimization; pub(crate) mod optimization;
+6 -8
View File
@@ -46,10 +46,7 @@ pub struct Cholesky<T: RealNumber, M: BaseMatrix<T>> {
impl<T: RealNumber, M: BaseMatrix<T>> Cholesky<T, M> { impl<T: RealNumber, M: BaseMatrix<T>> Cholesky<T, M> {
pub(crate) fn new(R: M) -> Cholesky<T, M> { pub(crate) fn new(R: M) -> Cholesky<T, M> {
Cholesky { Cholesky { R, t: PhantomData }
R: R,
t: PhantomData,
}
} }
/// Get lower triangular matrix. /// Get lower triangular matrix.
@@ -90,7 +87,8 @@ impl<T: RealNumber, M: BaseMatrix<T>> Cholesky<T, M> {
if bn != rn { if bn != rn {
return Err(Failed::because( return Err(Failed::because(
FailedError::SolutionFailed, FailedError::SolutionFailed,
&format!("Can't solve Ax = b for x. Number of rows in b != number of rows in R."), &"Can\'t solve Ax = b for x. Number of rows in b != number of rows in R."
.to_string(),
)); ));
} }
@@ -130,7 +128,7 @@ pub trait CholeskyDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
if m != n { if m != n {
return Err(Failed::because( return Err(Failed::because(
FailedError::DecompositionFailed, FailedError::DecompositionFailed,
&format!("Can't do Cholesky decomposition on a non-square matrix"), &"Can\'t do Cholesky decomposition on a non-square matrix".to_string(),
)); ));
} }
@@ -143,14 +141,14 @@ pub trait CholeskyDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
} }
s = (self.get(j, k) - s) / self.get(k, k); s = (self.get(j, k) - s) / self.get(k, k);
self.set(j, k, s); self.set(j, k, s);
d = d + s * s; d += s * s;
} }
d = self.get(j, j) - d; d = self.get(j, j) - d;
if d < T::zero() { if d < T::zero() {
return Err(Failed::because( return Err(Failed::because(
FailedError::DecompositionFailed, FailedError::DecompositionFailed,
&format!("The matrix is not positive definite."), &"The matrix is not positive definite.".to_string(),
)); ));
} }
+45 -45
View File
@@ -93,7 +93,7 @@ pub trait EVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
sort(&mut d, &mut e, &mut V); sort(&mut d, &mut e, &mut V);
} }
Ok(EVD { V: V, d: d, e: e }) Ok(EVD { V, d, e })
} }
} }
@@ -107,7 +107,7 @@ fn tred2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec
let mut scale = T::zero(); let mut scale = T::zero();
let mut h = T::zero(); let mut h = T::zero();
for k in 0..i { for k in 0..i {
scale = scale + d[k].abs(); scale += d[k].abs();
} }
if scale == T::zero() { if scale == T::zero() {
e[i] = d[i - 1]; e[i] = d[i - 1];
@@ -118,8 +118,8 @@ fn tred2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec
} }
} else { } else {
for k in 0..i { for k in 0..i {
d[k] = d[k] / scale; d[k] /= scale;
h = h + d[k] * d[k]; h += d[k] * d[k];
} }
let mut f = d[i - 1]; let mut f = d[i - 1];
let mut g = h.sqrt(); let mut g = h.sqrt();
@@ -127,7 +127,7 @@ fn tred2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec
g = -g; g = -g;
} }
e[i] = scale * g; e[i] = scale * g;
h = h - f * g; h -= f * g;
d[i - 1] = f - g; d[i - 1] = f - g;
for j in 0..i { for j in 0..i {
e[j] = T::zero(); e[j] = T::zero();
@@ -138,19 +138,19 @@ fn tred2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec
V.set(j, i, f); V.set(j, i, f);
g = e[j] + V.get(j, j) * f; g = e[j] + V.get(j, j) * f;
for k in j + 1..=i - 1 { for k in j + 1..=i - 1 {
g = g + V.get(k, j) * d[k]; g += V.get(k, j) * d[k];
e[k] = e[k] + V.get(k, j) * f; e[k] += V.get(k, j) * f;
} }
e[j] = g; e[j] = g;
} }
f = T::zero(); f = T::zero();
for j in 0..i { for j in 0..i {
e[j] = e[j] / h; e[j] /= h;
f = f + e[j] * d[j]; f += e[j] * d[j];
} }
let hh = f / (h + h); let hh = f / (h + h);
for j in 0..i { for j in 0..i {
e[j] = e[j] - hh * d[j]; e[j] -= hh * d[j];
} }
for j in 0..i { for j in 0..i {
f = d[j]; f = d[j];
@@ -176,7 +176,7 @@ fn tred2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec
for j in 0..=i { for j in 0..=i {
let mut g = T::zero(); let mut g = T::zero();
for k in 0..=i { for k in 0..=i {
g = g + V.get(k, i + 1) * V.get(k, j); g += V.get(k, i + 1) * V.get(k, j);
} }
for k in 0..=i { for k in 0..=i {
V.sub_element_mut(k, j, g * d[k]); V.sub_element_mut(k, j, g * d[k]);
@@ -239,9 +239,9 @@ fn tql2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec<
let dl1 = d[l + 1]; let dl1 = d[l + 1];
let mut h = g - d[l]; let mut h = g - d[l];
for i in l + 2..n { for i in l + 2..n {
d[i] = d[i] - h; d[i] -= h;
} }
f = f + h; f += h;
p = d[m]; p = d[m];
let mut c = T::one(); let mut c = T::one();
@@ -278,7 +278,7 @@ fn tql2<T: RealNumber, M: BaseMatrix<T>>(V: &mut M, d: &mut Vec<T>, e: &mut Vec<
} }
} }
} }
d[l] = d[l] + f; d[l] += f;
e[l] = T::zero(); e[l] = T::zero();
} }
@@ -321,8 +321,8 @@ fn balance<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<T> {
let mut c = T::zero(); let mut c = T::zero();
for j in 0..n { for j in 0..n {
if j != i { if j != i {
c = c + A.get(j, i).abs(); c += A.get(j, i).abs();
r = r + A.get(i, j).abs(); r += A.get(i, j).abs();
} }
} }
if c != T::zero() && r != T::zero() { if c != T::zero() && r != T::zero() {
@@ -330,18 +330,18 @@ fn balance<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<T> {
let mut f = T::one(); let mut f = T::one();
let s = c + r; let s = c + r;
while c < g { while c < g {
f = f * radix; f *= radix;
c = c * sqrdx; c *= sqrdx;
} }
g = r * radix; g = r * radix;
while c > g { while c > g {
f = f / radix; f /= radix;
c = c / sqrdx; c /= sqrdx;
} }
if (c + r) / f < t * s { if (c + r) / f < t * s {
done = false; done = false;
g = T::one() / f; g = T::one() / f;
scale[i] = scale[i] * f; scale[i] *= f;
for j in 0..n { for j in 0..n {
A.mul_element_mut(i, j, g); A.mul_element_mut(i, j, g);
} }
@@ -353,7 +353,7 @@ fn balance<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<T> {
} }
} }
return scale; scale
} }
fn elmhes<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<usize> { fn elmhes<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<usize> {
@@ -386,7 +386,7 @@ fn elmhes<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<usize> {
for i in (m + 1)..n { for i in (m + 1)..n {
let mut y = A.get(i, m - 1); let mut y = A.get(i, m - 1);
if y != T::zero() { if y != T::zero() {
y = y / x; y /= x;
A.set(i, m - 1, y); A.set(i, m - 1, y);
for j in m..n { for j in m..n {
A.sub_element_mut(i, j, y * A.get(m, j)); A.sub_element_mut(i, j, y * A.get(m, j));
@@ -399,7 +399,7 @@ fn elmhes<T: RealNumber, M: BaseMatrix<T>>(A: &mut M) -> Vec<usize> {
} }
} }
return perm; perm
} }
fn eltran<T: RealNumber, M: BaseMatrix<T>>(A: &M, V: &mut M, perm: &Vec<usize>) { fn eltran<T: RealNumber, M: BaseMatrix<T>>(A: &M, V: &mut M, perm: &Vec<usize>) {
@@ -430,7 +430,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
for i in 0..n { for i in 0..n {
for j in i32::max(i as i32 - 1, 0)..n as i32 { for j in i32::max(i as i32 - 1, 0)..n as i32 {
anorm = anorm + A.get(i, j as usize).abs(); anorm += A.get(i, j as usize).abs();
} }
} }
@@ -467,7 +467,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
p = T::half() * (y - x); p = T::half() * (y - x);
q = p * p + w; q = p * p + w;
z = q.abs().sqrt(); z = q.abs().sqrt();
x = x + t; x += t;
A.set(nn, nn, x); A.set(nn, nn, x);
A.set(nn - 1, nn - 1, y + t); A.set(nn - 1, nn - 1, y + t);
if q >= T::zero() { if q >= T::zero() {
@@ -482,8 +482,8 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
p = x / s; p = x / s;
q = z / s; q = z / s;
r = (p * p + q * q).sqrt(); r = (p * p + q * q).sqrt();
p = p / r; p /= r;
q = q / r; q /= r;
for j in nn - 1..n { for j in nn - 1..n {
z = A.get(nn - 1, j); z = A.get(nn - 1, j);
A.set(nn - 1, j, q * z + p * A.get(nn, j)); A.set(nn - 1, j, q * z + p * A.get(nn, j));
@@ -516,7 +516,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
panic!("Too many iterations in hqr"); panic!("Too many iterations in hqr");
} }
if its == 10 || its == 20 { if its == 10 || its == 20 {
t = t + x; t += x;
for i in 0..nn + 1 { for i in 0..nn + 1 {
A.sub_element_mut(i, i, x); A.sub_element_mut(i, i, x);
} }
@@ -535,9 +535,9 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
q = A.get(m + 1, m + 1) - z - r - s; q = A.get(m + 1, m + 1) - z - r - s;
r = A.get(m + 2, m + 1); r = A.get(m + 2, m + 1);
s = p.abs() + q.abs() + r.abs(); s = p.abs() + q.abs() + r.abs();
p = p / s; p /= s;
q = q / s; q /= s;
r = r / s; r /= s;
if m == l { if m == l {
break; break;
} }
@@ -565,9 +565,9 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
} }
x = p.abs() + q.abs() + r.abs(); x = p.abs() + q.abs() + r.abs();
if x != T::zero() { if x != T::zero() {
p = p / x; p /= x;
q = q / x; q /= x;
r = r / x; r /= x;
} }
} }
let s = (p * p + q * q + r * r).sqrt().copysign(p); let s = (p * p + q * q + r * r).sqrt().copysign(p);
@@ -579,16 +579,16 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
} else { } else {
A.set(k, k - 1, -s * x); A.set(k, k - 1, -s * x);
} }
p = p + s; p += s;
x = p / s; x = p / s;
y = q / s; y = q / s;
z = r / s; z = r / s;
q = q / p; q /= p;
r = r / p; r /= p;
for j in k..n { for j in k..n {
p = A.get(k, j) + q * A.get(k + 1, j); p = A.get(k, j) + q * A.get(k + 1, j);
if k + 1 != nn { if k + 1 != nn {
p = p + r * A.get(k + 2, j); p += r * A.get(k + 2, j);
A.sub_element_mut(k + 2, j, p * z); A.sub_element_mut(k + 2, j, p * z);
} }
A.sub_element_mut(k + 1, j, p * y); A.sub_element_mut(k + 1, j, p * y);
@@ -603,7 +603,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
for i in 0..mmin + 1 { for i in 0..mmin + 1 {
p = x * A.get(i, k) + y * A.get(i, k + 1); p = x * A.get(i, k) + y * A.get(i, k + 1);
if k + 1 != nn { if k + 1 != nn {
p = p + z * A.get(i, k + 2); p += z * A.get(i, k + 2);
A.sub_element_mut(i, k + 2, p * r); A.sub_element_mut(i, k + 2, p * r);
} }
A.sub_element_mut(i, k + 1, p * q); A.sub_element_mut(i, k + 1, p * q);
@@ -612,7 +612,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
for i in 0..n { for i in 0..n {
p = x * V.get(i, k) + y * V.get(i, k + 1); p = x * V.get(i, k) + y * V.get(i, k + 1);
if k + 1 != nn { if k + 1 != nn {
p = p + z * V.get(i, k + 2); p += z * V.get(i, k + 2);
V.sub_element_mut(i, k + 2, p * r); V.sub_element_mut(i, k + 2, p * r);
} }
V.sub_element_mut(i, k + 1, p * q); V.sub_element_mut(i, k + 1, p * q);
@@ -642,7 +642,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
let w = A.get(i, i) - p; let w = A.get(i, i) - p;
r = T::zero(); r = T::zero();
for j in m..=nn { for j in m..=nn {
r = r + A.get(i, j) * A.get(j, nn); r += A.get(i, j) * A.get(j, nn);
} }
if e[i] < T::zero() { if e[i] < T::zero() {
z = w; z = w;
@@ -701,8 +701,8 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
let mut ra = T::zero(); let mut ra = T::zero();
let mut sa = T::zero(); let mut sa = T::zero();
for j in m..=nn { for j in m..=nn {
ra = ra + A.get(i, j) * A.get(j, na); ra += A.get(i, j) * A.get(j, na);
sa = sa + A.get(i, j) * A.get(j, nn); sa += A.get(i, j) * A.get(j, nn);
} }
if e[i] < T::zero() { if e[i] < T::zero() {
z = w; z = w;
@@ -766,7 +766,7 @@ fn hqr2<T: RealNumber, M: BaseMatrix<T>>(A: &mut M, V: &mut M, d: &mut Vec<T>, e
for i in 0..n { for i in 0..n {
z = T::zero(); z = T::zero();
for k in 0..=j { for k in 0..=j {
z = z + V.get(i, k) * A.get(k, j); z += V.get(i, k) * A.get(k, j);
} }
V.set(i, j, z); V.set(i, j, z);
} }
+12 -15
View File
@@ -33,6 +33,7 @@
//! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> //! <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::cmp::Ordering;
use std::fmt::Debug; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -63,10 +64,10 @@ impl<T: RealNumber, M: BaseMatrix<T>> LU<T, M> {
} }
LU { LU {
LU: LU, LU,
pivot: pivot, pivot,
pivot_sign: pivot_sign, pivot_sign,
singular: singular, singular,
phantom: PhantomData, phantom: PhantomData,
} }
} }
@@ -78,12 +79,10 @@ impl<T: RealNumber, M: BaseMatrix<T>> LU<T, M> {
for i in 0..n_rows { for i in 0..n_rows {
for j in 0..n_cols { for j in 0..n_cols {
if i > j { match i.cmp(&j) {
L.set(i, j, self.LU.get(i, j)); Ordering::Greater => L.set(i, j, self.LU.get(i, j)),
} else if i == j { Ordering::Equal => L.set(i, j, T::one()),
L.set(i, j, T::one()); Ordering::Less => L.set(i, j, T::zero()),
} else {
L.set(i, j, T::zero());
} }
} }
} }
@@ -220,10 +219,10 @@ pub trait LUDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
let kmax = usize::min(i, j); let kmax = usize::min(i, j);
let mut s = T::zero(); let mut s = T::zero();
for k in 0..kmax { for k in 0..kmax {
s = s + self.get(i, k) * LUcolj[k]; s += self.get(i, k) * LUcolj[k];
} }
LUcolj[i] = LUcolj[i] - s; LUcolj[i] -= s;
self.set(i, j, LUcolj[i]); self.set(i, j, LUcolj[i]);
} }
@@ -239,9 +238,7 @@ pub trait LUDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
self.set(p, k, self.get(j, k)); self.set(p, k, self.get(j, k));
self.set(j, k, t); self.set(j, k, t);
} }
let k = piv[p]; piv.swap(p, j);
piv[p] = piv[j];
piv[j] = k;
pivsign = -pivsign; pivsign = -pivsign;
} }
+7 -2
View File
@@ -78,6 +78,11 @@ pub trait BaseVector<T: RealNumber>: Clone + Debug {
/// Get number of elevemnt in the vector /// Get number of elevemnt in the vector
fn len(&self) -> usize; fn len(&self) -> usize;
/// Returns true if the vector is empty.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Return a vector with the elements of the one-dimensional array. /// Return a vector with the elements of the one-dimensional array.
fn to_vec(&self) -> Vec<T>; fn to_vec(&self) -> Vec<T>;
@@ -542,9 +547,9 @@ pub trait Matrix<T: RealNumber>:
{ {
} }
pub(crate) fn row_iter<F: RealNumber, M: BaseMatrix<F>>(m: &M) -> RowIter<F, M> { pub(crate) fn row_iter<F: RealNumber, M: BaseMatrix<F>>(m: &M) -> RowIter<'_, F, M> {
RowIter { RowIter {
m: m, m,
pos: 0, pos: 0,
max_pos: m.shape().0, max_pos: m.shape().0,
phantom: PhantomData, phantom: PhantomData,
+44 -46
View File
@@ -1,4 +1,3 @@
extern crate num;
use std::fmt; use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -31,8 +30,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
} }
fn to_vec(&self) -> Vec<T> { fn to_vec(&self) -> Vec<T> {
let v = self.clone(); self.clone()
v
} }
fn zeros(len: usize) -> Self { fn zeros(len: usize) -> Self {
@@ -54,7 +52,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
let mut result = T::zero(); let mut result = T::zero();
for i in 0..self.len() { for i in 0..self.len() {
result = result + self[i] * other[i]; result += self[i] * other[i];
} }
result result
@@ -64,7 +62,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + *xi * *xi; norm += *xi * *xi;
} }
norm.sqrt() norm.sqrt()
@@ -83,7 +81,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -91,19 +89,19 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
} }
fn div_element_mut(&mut self, pos: usize, x: T) { fn div_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] / x; self[pos] /= x;
} }
fn mul_element_mut(&mut self, pos: usize, x: T) { fn mul_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] * x; self[pos] *= x;
} }
fn add_element_mut(&mut self, pos: usize, x: T) { fn add_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] + x self[pos] += x
} }
fn sub_element_mut(&mut self, pos: usize, x: T) { fn sub_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] - x; self[pos] -= x;
} }
fn add_mut(&mut self, other: &Self) -> &Self { fn add_mut(&mut self, other: &Self) -> &Self {
@@ -166,7 +164,7 @@ impl<T: RealNumber> BaseVector<T> for Vec<T> {
fn sum(&self) -> T { fn sum(&self) -> T {
let mut sum = T::zero(); let mut sum = T::zero();
for i in 0..self.len() { for i in 0..self.len() {
sum = sum + self[i]; sum += self[i];
} }
sum sum
} }
@@ -198,7 +196,7 @@ pub struct DenseMatrixIterator<'a, T: RealNumber> {
} }
impl<T: RealNumber> fmt::Display for DenseMatrix<T> { impl<T: RealNumber> fmt::Display for DenseMatrix<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut rows: Vec<Vec<f64>> = Vec::new(); let mut rows: Vec<Vec<f64>> = Vec::new();
for r in 0..self.nrows { for r in 0..self.nrows {
rows.push( rows.push(
@@ -217,15 +215,15 @@ impl<T: RealNumber> DenseMatrix<T> {
/// `values` should be in column-major order. /// `values` should be in column-major order.
pub fn new(nrows: usize, ncols: usize, values: Vec<T>) -> Self { pub fn new(nrows: usize, ncols: usize, values: Vec<T>) -> Self {
DenseMatrix { DenseMatrix {
ncols: ncols, ncols,
nrows: nrows, nrows,
values: values, values,
} }
} }
/// New instance of `DenseMatrix` from 2d array. /// New instance of `DenseMatrix` from 2d array.
pub fn from_2d_array(values: &[&[T]]) -> Self { pub fn from_2d_array(values: &[&[T]]) -> Self {
DenseMatrix::from_2d_vec(&values.into_iter().map(|row| Vec::from(*row)).collect()) DenseMatrix::from_2d_vec(&values.iter().map(|row| Vec::from(*row)).collect())
} }
/// New instance of `DenseMatrix` from 2d vector. /// New instance of `DenseMatrix` from 2d vector.
@@ -236,8 +234,8 @@ impl<T: RealNumber> DenseMatrix<T> {
.unwrap_or_else(|| panic!("Cannot create 2d matrix from an empty vector")) .unwrap_or_else(|| panic!("Cannot create 2d matrix from an empty vector"))
.len(); .len();
let mut m = DenseMatrix { let mut m = DenseMatrix {
ncols: ncols, ncols,
nrows: nrows, nrows,
values: vec![T::zero(); ncols * nrows], values: vec![T::zero(); ncols * nrows],
}; };
for row in 0..nrows { for row in 0..nrows {
@@ -262,8 +260,8 @@ impl<T: RealNumber> DenseMatrix<T> {
/// * `values` - values to initialize the matrix. /// * `values` - values to initialize the matrix.
pub fn from_vec(nrows: usize, ncols: usize, values: &Vec<T>) -> DenseMatrix<T> { pub fn from_vec(nrows: usize, ncols: usize, values: &Vec<T>) -> DenseMatrix<T> {
let mut m = DenseMatrix { let mut m = DenseMatrix {
ncols: ncols, ncols,
nrows: nrows, nrows,
values: vec![T::zero(); ncols * nrows], values: vec![T::zero(); ncols * nrows],
}; };
for row in 0..nrows { for row in 0..nrows {
@@ -286,7 +284,7 @@ impl<T: RealNumber> DenseMatrix<T> {
DenseMatrix { DenseMatrix {
ncols: values.len(), ncols: values.len(),
nrows: 1, nrows: 1,
values: values, values,
} }
} }
@@ -302,13 +300,13 @@ impl<T: RealNumber> DenseMatrix<T> {
DenseMatrix { DenseMatrix {
ncols: 1, ncols: 1,
nrows: values.len(), nrows: values.len(),
values: values, values,
} }
} }
/// Creates new column vector (_1xN_ matrix) from a vector. /// Creates new column vector (_1xN_ matrix) from a vector.
/// * `values` - values to initialize the matrix. /// * `values` - values to initialize the matrix.
pub fn iter<'a>(&'a self) -> DenseMatrixIterator<'a, T> { pub fn iter(&self) -> DenseMatrixIterator<'_, T> {
DenseMatrixIterator { DenseMatrixIterator {
cur_c: 0, cur_c: 0,
cur_r: 0, cur_r: 0,
@@ -357,7 +355,7 @@ impl<'de, T: RealNumber + fmt::Debug + Deserialize<'de>> Deserialize<'de> for De
impl<'a, T: RealNumber + fmt::Debug + Deserialize<'a>> Visitor<'a> for DenseMatrixVisitor<T> { impl<'a, T: RealNumber + fmt::Debug + Deserialize<'a>> Visitor<'a> for DenseMatrixVisitor<T> {
type Value = DenseMatrix<T>; type Value = DenseMatrix<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct DenseMatrix") formatter.write_str("struct DenseMatrix")
} }
@@ -413,7 +411,7 @@ impl<'de, T: RealNumber + fmt::Debug + Deserialize<'de>> Deserialize<'de> for De
} }
} }
const FIELDS: &'static [&'static str] = &["nrows", "ncols", "values"]; const FIELDS: &[&str] = &["nrows", "ncols", "values"];
deserializer.deserialize_struct( deserializer.deserialize_struct(
"DenseMatrix", "DenseMatrix",
FIELDS, FIELDS,
@@ -565,7 +563,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
matrix.set(i, i, T::one()); matrix.set(i, i, T::one());
} }
return matrix; matrix
} }
fn shape(&self) -> (usize, usize) { fn shape(&self) -> (usize, usize) {
@@ -617,7 +615,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
for c in 0..other.ncols { for c in 0..other.ncols {
let mut s = T::zero(); let mut s = T::zero();
for i in 0..inner_d { for i in 0..inner_d {
s = s + self.get(r, i) * other.get(i, c); s += self.get(r, i) * other.get(i, c);
} }
result.set(r, c, s); result.set(r, c, s);
} }
@@ -636,7 +634,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
let mut result = T::zero(); let mut result = T::zero();
for i in 0..(self.nrows * self.ncols) { for i in 0..(self.nrows * self.ncols) {
result = result + self.values[i] * other.values[i]; result += self.values[i] * other.values[i];
} }
result result
@@ -730,19 +728,19 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
} }
fn div_element_mut(&mut self, row: usize, col: usize, x: T) { fn div_element_mut(&mut self, row: usize, col: usize, x: T) {
self.values[col * self.nrows + row] = self.values[col * self.nrows + row] / x; self.values[col * self.nrows + row] /= x;
} }
fn mul_element_mut(&mut self, row: usize, col: usize, x: T) { fn mul_element_mut(&mut self, row: usize, col: usize, x: T) {
self.values[col * self.nrows + row] = self.values[col * self.nrows + row] * x; self.values[col * self.nrows + row] *= x;
} }
fn add_element_mut(&mut self, row: usize, col: usize, x: T) { fn add_element_mut(&mut self, row: usize, col: usize, x: T) {
self.values[col * self.nrows + row] = self.values[col * self.nrows + row] + x self.values[col * self.nrows + row] += x
} }
fn sub_element_mut(&mut self, row: usize, col: usize, x: T) { fn sub_element_mut(&mut self, row: usize, col: usize, x: T) {
self.values[col * self.nrows + row] = self.values[col * self.nrows + row] - x; self.values[col * self.nrows + row] -= x;
} }
fn transpose(&self) -> Self { fn transpose(&self) -> Self {
@@ -762,9 +760,9 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
fn rand(nrows: usize, ncols: usize) -> Self { fn rand(nrows: usize, ncols: usize) -> Self {
let values: Vec<T> = (0..nrows * ncols).map(|_| T::rand()).collect(); let values: Vec<T> = (0..nrows * ncols).map(|_| T::rand()).collect();
DenseMatrix { DenseMatrix {
ncols: ncols, ncols,
nrows: nrows, nrows,
values: values, values,
} }
} }
@@ -772,7 +770,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.values.iter() { for xi in self.values.iter() {
norm = norm + *xi * *xi; norm += *xi * *xi;
} }
norm.sqrt() norm.sqrt()
@@ -793,7 +791,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.values.iter() { for xi in self.values.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -805,12 +803,12 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
for r in 0..self.nrows { for r in 0..self.nrows {
for c in 0..self.ncols { for c in 0..self.ncols {
mean[c] = mean[c] + self.get(r, c); mean[c] += self.get(r, c);
} }
} }
for i in 0..mean.len() { for i in 0..mean.len() {
mean[i] = mean[i] / T::from(self.nrows).unwrap(); mean[i] /= T::from(self.nrows).unwrap();
} }
mean mean
@@ -818,28 +816,28 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
fn add_scalar_mut(&mut self, scalar: T) -> &Self { fn add_scalar_mut(&mut self, scalar: T) -> &Self {
for i in 0..self.values.len() { for i in 0..self.values.len() {
self.values[i] = self.values[i] + scalar; self.values[i] += scalar;
} }
self self
} }
fn sub_scalar_mut(&mut self, scalar: T) -> &Self { fn sub_scalar_mut(&mut self, scalar: T) -> &Self {
for i in 0..self.values.len() { for i in 0..self.values.len() {
self.values[i] = self.values[i] - scalar; self.values[i] -= scalar;
} }
self self
} }
fn mul_scalar_mut(&mut self, scalar: T) -> &Self { fn mul_scalar_mut(&mut self, scalar: T) -> &Self {
for i in 0..self.values.len() { for i in 0..self.values.len() {
self.values[i] = self.values[i] * scalar; self.values[i] *= scalar;
} }
self self
} }
fn div_scalar_mut(&mut self, scalar: T) -> &Self { fn div_scalar_mut(&mut self, scalar: T) -> &Self {
for i in 0..self.values.len() { for i in 0..self.values.len() {
self.values[i] = self.values[i] / scalar; self.values[i] /= scalar;
} }
self self
} }
@@ -905,7 +903,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
fn sum(&self) -> T { fn sum(&self) -> T {
let mut sum = T::zero(); let mut sum = T::zero();
for i in 0..self.values.len() { for i in 0..self.values.len() {
sum = sum + self.values[i]; sum += self.values[i];
} }
sum sum
} }
@@ -937,7 +935,7 @@ impl<T: RealNumber> BaseMatrix<T> for DenseMatrix<T> {
for c in 0..self.ncols { for c in 0..self.ncols {
let p = (self.get(r, c) - max).exp(); let p = (self.get(r, c) - max).exp();
self.set(r, c, p); self.set(r, c, p);
z = z + p; z += p;
} }
} }
for r in 0..self.nrows { for r in 0..self.nrows {
@@ -1061,7 +1059,7 @@ mod tests {
DenseMatrix::new(1, 3, vec![1., 2., 3.]) DenseMatrix::new(1, 3, vec![1., 2., 3.])
); );
assert_eq!( assert_eq!(
DenseMatrix::from_row_vector(vec.clone()).to_row_vector(), DenseMatrix::from_row_vector(vec).to_row_vector(),
vec![1., 2., 3.] vec![1., 2., 3.]
); );
} }
+11 -15
View File
@@ -65,7 +65,7 @@ impl<T: RealNumber + 'static> BaseVector<T> for MatrixMN<T, U1, Dynamic> {
} }
fn to_vec(&self) -> Vec<T> { fn to_vec(&self) -> Vec<T> {
self.row(0).iter().map(|v| *v).collect() self.row(0).iter().copied().collect()
} }
fn zeros(len: usize) -> Self { fn zeros(len: usize) -> Self {
@@ -113,7 +113,7 @@ impl<T: RealNumber + 'static> BaseVector<T> for MatrixMN<T, U1, Dynamic> {
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -175,7 +175,7 @@ impl<T: RealNumber + 'static> BaseVector<T> for MatrixMN<T, U1, Dynamic> {
} }
fn unique(&self) -> Vec<T> { fn unique(&self) -> Vec<T> {
let mut result: Vec<T> = self.iter().map(|v| *v).collect(); let mut result: Vec<T> = self.iter().copied().collect();
result.sort_by(|a, b| a.partial_cmp(b).unwrap()); result.sort_by(|a, b| a.partial_cmp(b).unwrap());
result.dedup(); result.dedup();
result result
@@ -200,7 +200,7 @@ impl<T: RealNumber + Scalar + AddAssign + SubAssign + MulAssign + DivAssign + Su
} }
fn get_row_as_vec(&self, row: usize) -> Vec<T> { fn get_row_as_vec(&self, row: usize) -> Vec<T> {
self.row(row).iter().map(|v| *v).collect() self.row(row).iter().copied().collect()
} }
fn get_row(&self, row: usize) -> Self::RowVector { fn get_row(&self, row: usize) -> Self::RowVector {
@@ -208,22 +208,18 @@ impl<T: RealNumber + Scalar + AddAssign + SubAssign + MulAssign + DivAssign + Su
} }
fn copy_row_as_vec(&self, row: usize, result: &mut Vec<T>) { fn copy_row_as_vec(&self, row: usize, result: &mut Vec<T>) {
let mut r = 0; for (r, e) in self.row(row).iter().enumerate() {
for e in self.row(row).iter() {
result[r] = *e; result[r] = *e;
r += 1;
} }
} }
fn get_col_as_vec(&self, col: usize) -> Vec<T> { fn get_col_as_vec(&self, col: usize) -> Vec<T> {
self.column(col).iter().map(|v| *v).collect() self.column(col).iter().copied().collect()
} }
fn copy_col_as_vec(&self, col: usize, result: &mut Vec<T>) { fn copy_col_as_vec(&self, col: usize, result: &mut Vec<T>) {
let mut r = 0; for (c, e) in self.column(col).iter().enumerate() {
for e in self.column(col).iter() { result[c] = *e;
result[r] = *e;
r += 1;
} }
} }
@@ -369,7 +365,7 @@ impl<T: RealNumber + Scalar + AddAssign + SubAssign + MulAssign + DivAssign + Su
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -478,7 +474,7 @@ impl<T: RealNumber + Scalar + AddAssign + SubAssign + MulAssign + DivAssign + Su
for c in 0..self.ncols() { for c in 0..self.ncols() {
let p = (self[(r, c)] - max).exp(); let p = (self[(r, c)] - max).exp();
self.set(r, c, p); self.set(r, c, p);
z = z + p; z += p;
} }
} }
for r in 0..self.nrows() { for r in 0..self.nrows() {
@@ -515,7 +511,7 @@ impl<T: RealNumber + Scalar + AddAssign + SubAssign + MulAssign + DivAssign + Su
} }
fn unique(&self) -> Vec<T> { fn unique(&self) -> Vec<T> {
let mut result: Vec<T> = self.iter().map(|v| *v).collect(); let mut result: Vec<T> = self.iter().copied().collect();
result.sort_by(|a, b| a.partial_cmp(b).unwrap()); result.sort_by(|a, b| a.partial_cmp(b).unwrap());
result.dedup(); result.dedup();
result result
+14 -18
View File
@@ -118,7 +118,7 @@ impl<T: RealNumber + ScalarOperand> BaseVector<T> for ArrayBase<OwnedRepr<T>, Ix
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -126,19 +126,19 @@ impl<T: RealNumber + ScalarOperand> BaseVector<T> for ArrayBase<OwnedRepr<T>, Ix
} }
fn div_element_mut(&mut self, pos: usize, x: T) { fn div_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] / x; self[pos] /= x;
} }
fn mul_element_mut(&mut self, pos: usize, x: T) { fn mul_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] * x; self[pos] *= x;
} }
fn add_element_mut(&mut self, pos: usize, x: T) { fn add_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] + x; self[pos] += x;
} }
fn sub_element_mut(&mut self, pos: usize, x: T) { fn sub_element_mut(&mut self, pos: usize, x: T) {
self[pos] = self[pos] - x; self[pos] -= x;
} }
fn approximate_eq(&self, other: &Self, error: T) -> bool { fn approximate_eq(&self, other: &Self, error: T) -> bool {
@@ -205,10 +205,8 @@ impl<T: RealNumber + ScalarOperand + AddAssign + SubAssign + MulAssign + DivAssi
} }
fn copy_row_as_vec(&self, row: usize, result: &mut Vec<T>) { fn copy_row_as_vec(&self, row: usize, result: &mut Vec<T>) {
let mut r = 0; for (r, e) in self.row(row).iter().enumerate() {
for e in self.row(row).iter() {
result[r] = *e; result[r] = *e;
r += 1;
} }
} }
@@ -217,10 +215,8 @@ impl<T: RealNumber + ScalarOperand + AddAssign + SubAssign + MulAssign + DivAssi
} }
fn copy_col_as_vec(&self, col: usize, result: &mut Vec<T>) { fn copy_col_as_vec(&self, col: usize, result: &mut Vec<T>) {
let mut r = 0; for (c, e) in self.column(col).iter().enumerate() {
for e in self.column(col).iter() { result[c] = *e;
result[r] = *e;
r += 1;
} }
} }
@@ -348,7 +344,7 @@ impl<T: RealNumber + ScalarOperand + AddAssign + SubAssign + MulAssign + DivAssi
let mut norm = T::zero(); let mut norm = T::zero();
for xi in self.iter() { for xi in self.iter() {
norm = norm + xi.abs().powf(p); norm += xi.abs().powf(p);
} }
norm.powf(T::one() / p) norm.powf(T::one() / p)
@@ -360,19 +356,19 @@ impl<T: RealNumber + ScalarOperand + AddAssign + SubAssign + MulAssign + DivAssi
} }
fn div_element_mut(&mut self, row: usize, col: usize, x: T) { fn div_element_mut(&mut self, row: usize, col: usize, x: T) {
self[[row, col]] = self[[row, col]] / x; self[[row, col]] /= x;
} }
fn mul_element_mut(&mut self, row: usize, col: usize, x: T) { fn mul_element_mut(&mut self, row: usize, col: usize, x: T) {
self[[row, col]] = self[[row, col]] * x; self[[row, col]] *= x;
} }
fn add_element_mut(&mut self, row: usize, col: usize, x: T) { fn add_element_mut(&mut self, row: usize, col: usize, x: T) {
self[[row, col]] = self[[row, col]] + x; self[[row, col]] += x;
} }
fn sub_element_mut(&mut self, row: usize, col: usize, x: T) { fn sub_element_mut(&mut self, row: usize, col: usize, x: T) {
self[[row, col]] = self[[row, col]] - x; self[[row, col]] -= x;
} }
fn negative_mut(&mut self) { fn negative_mut(&mut self) {
@@ -426,7 +422,7 @@ impl<T: RealNumber + ScalarOperand + AddAssign + SubAssign + MulAssign + DivAssi
for c in 0..self.ncols() { for c in 0..self.ncols() {
let p = (self[(r, c)] - max).exp(); let p = (self[(r, c)] - max).exp();
self.set(r, c, p); self.set(r, c, p);
z = z + p; z += p;
} }
} }
for r in 0..self.nrows() { for r in 0..self.nrows() {
+6 -10
View File
@@ -51,11 +51,7 @@ impl<T: RealNumber, M: BaseMatrix<T>> QR<T, M> {
} }
} }
QR { QR { QR, tau, singular }
QR: QR,
tau: tau,
singular: singular,
}
} }
/// Get upper triangular matrix. /// Get upper triangular matrix.
@@ -68,7 +64,7 @@ impl<T: RealNumber, M: BaseMatrix<T>> QR<T, M> {
R.set(i, j, self.QR.get(i, j)); R.set(i, j, self.QR.get(i, j));
} }
} }
return R; R
} }
/// Get an orthogonal matrix. /// Get an orthogonal matrix.
@@ -82,7 +78,7 @@ impl<T: RealNumber, M: BaseMatrix<T>> QR<T, M> {
if self.QR.get(k, k) != T::zero() { if self.QR.get(k, k) != T::zero() {
let mut s = T::zero(); let mut s = T::zero();
for i in k..m { for i in k..m {
s = s + self.QR.get(i, k) * Q.get(i, j); s += self.QR.get(i, k) * Q.get(i, j);
} }
s = -s / self.QR.get(k, k); s = -s / self.QR.get(k, k);
for i in k..m { for i in k..m {
@@ -96,7 +92,7 @@ impl<T: RealNumber, M: BaseMatrix<T>> QR<T, M> {
k -= 1; k -= 1;
} }
} }
return Q; Q
} }
fn solve(&self, mut b: M) -> Result<M, Failed> { fn solve(&self, mut b: M) -> Result<M, Failed> {
@@ -118,7 +114,7 @@ impl<T: RealNumber, M: BaseMatrix<T>> QR<T, M> {
for j in 0..b_ncols { for j in 0..b_ncols {
let mut s = T::zero(); let mut s = T::zero();
for i in k..m { for i in k..m {
s = s + self.QR.get(i, k) * b.get(i, j); s += self.QR.get(i, k) * b.get(i, j);
} }
s = -s / self.QR.get(k, k); s = -s / self.QR.get(k, k);
for i in k..m { for i in k..m {
@@ -175,7 +171,7 @@ pub trait QRDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
for j in k + 1..n { for j in k + 1..n {
let mut s = T::zero(); let mut s = T::zero();
for i in k..m { for i in k..m {
s = s + self.get(i, k) * self.get(i, j); s += self.get(i, k) * self.get(i, j);
} }
s = -s / self.get(k, k); s = -s / self.get(k, k);
for i in k..m { for i in k..m {
+20 -20
View File
@@ -106,13 +106,13 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
if i < m { if i < m {
for k in i..m { for k in i..m {
scale = scale + U.get(k, i).abs(); scale += U.get(k, i).abs();
} }
if scale.abs() > T::epsilon() { if scale.abs() > T::epsilon() {
for k in i..m { for k in i..m {
U.div_element_mut(k, i, scale); U.div_element_mut(k, i, scale);
s = s + U.get(k, i) * U.get(k, i); s += U.get(k, i) * U.get(k, i);
} }
let mut f = U.get(i, i); let mut f = U.get(i, i);
@@ -122,7 +122,7 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
for j in l - 1..n { for j in l - 1..n {
s = T::zero(); s = T::zero();
for k in i..m { for k in i..m {
s = s + U.get(k, i) * U.get(k, j); s += U.get(k, i) * U.get(k, j);
} }
f = s / h; f = s / h;
for k in i..m { for k in i..m {
@@ -140,15 +140,15 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
let mut s = T::zero(); let mut s = T::zero();
scale = T::zero(); scale = T::zero();
if i + 1 <= m && i + 1 != n { if i < m && i + 1 != n {
for k in l - 1..n { for k in l - 1..n {
scale = scale + U.get(i, k).abs(); scale += U.get(i, k).abs();
} }
if scale.abs() > T::epsilon() { if scale.abs() > T::epsilon() {
for k in l - 1..n { for k in l - 1..n {
U.div_element_mut(i, k, scale); U.div_element_mut(i, k, scale);
s = s + U.get(i, k) * U.get(i, k); s += U.get(i, k) * U.get(i, k);
} }
let f = U.get(i, l - 1); let f = U.get(i, l - 1);
@@ -163,7 +163,7 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
for j in l - 1..m { for j in l - 1..m {
s = T::zero(); s = T::zero();
for k in l - 1..n { for k in l - 1..n {
s = s + U.get(j, k) * U.get(i, k); s += U.get(j, k) * U.get(i, k);
} }
for k in l - 1..n { for k in l - 1..n {
@@ -189,7 +189,7 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
for j in l..n { for j in l..n {
let mut s = T::zero(); let mut s = T::zero();
for k in l..n { for k in l..n {
s = s + U.get(i, k) * v.get(k, j); s += U.get(i, k) * v.get(k, j);
} }
for k in l..n { for k in l..n {
v.add_element_mut(k, j, s * v.get(k, i)); v.add_element_mut(k, j, s * v.get(k, i));
@@ -218,7 +218,7 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
for j in l..n { for j in l..n {
let mut s = T::zero(); let mut s = T::zero();
for k in l..m { for k in l..m {
s = s + U.get(k, i) * U.get(k, j); s += U.get(k, i) * U.get(k, j);
} }
let f = (s / U.get(i, i)) * g; let f = (s / U.get(i, i)) * g;
for k in i..m { for k in i..m {
@@ -316,7 +316,7 @@ pub trait SVDDecomposableMatrix<T: RealNumber>: BaseMatrix<T> {
f = x * c + g * s; f = x * c + g * s;
g = g * c - x * s; g = g * c - x * s;
h = y * s; h = y * s;
y = y * c; y *= c;
for jj in 0..n { for jj in 0..n {
x = v.get(jj, j); x = v.get(jj, j);
@@ -431,13 +431,13 @@ impl<T: RealNumber, M: SVDDecomposableMatrix<T>> SVD<T, M> {
let full = s.len() == m.min(n); let full = s.len() == m.min(n);
let tol = T::half() * (T::from(m + n).unwrap() + T::one()).sqrt() * s[0] * T::epsilon(); let tol = T::half() * (T::from(m + n).unwrap() + T::one()).sqrt() * s[0] * T::epsilon();
SVD { SVD {
U: U, U,
V: V, V,
s: s, s,
full: full, full,
m: m, m,
n: n, n,
tol: tol, tol,
} }
} }
@@ -458,9 +458,9 @@ impl<T: RealNumber, M: SVDDecomposableMatrix<T>> SVD<T, M> {
let mut r = T::zero(); let mut r = T::zero();
if self.s[j] > self.tol { if self.s[j] > self.tol {
for i in 0..self.m { for i in 0..self.m {
r = r + self.U.get(i, j) * b.get(i, k); r += self.U.get(i, j) * b.get(i, k);
} }
r = r / self.s[j]; r /= self.s[j];
} }
tmp[j] = r; tmp[j] = r;
} }
@@ -468,7 +468,7 @@ impl<T: RealNumber, M: SVDDecomposableMatrix<T>> SVD<T, M> {
for j in 0..self.n { for j in 0..self.n {
let mut r = T::zero(); let mut r = T::zero();
for jj in 0..self.n { for jj in 0..self.n {
r = r + self.V.get(j, jj) * tmp[jj]; r += self.V.get(j, jj) * tmp[jj];
} }
b.set(j, k, r); b.set(j, k, r);
} }
+3 -3
View File
@@ -123,9 +123,9 @@ impl<T: RealNumber, M: Matrix<T>> LinearRegression<T, M> {
let (y_nrows, _) = b.shape(); let (y_nrows, _) = b.shape();
if x_nrows != y_nrows { if x_nrows != y_nrows {
return Err(Failed::fit(&format!( return Err(Failed::fit(
"Number of rows of X doesn't match number of rows of Y" &"Number of rows of X doesn\'t match number of rows of Y".to_string(),
))); ));
} }
let a = x.h_stack(&M::ones(x_nrows, 1)); let a = x.h_stack(&M::ones(x_nrows, 1));
+15 -13
View File
@@ -84,7 +84,7 @@ trait ObjectiveFunction<T: RealNumber, M: Matrix<T>> {
let mut sum = T::zero(); let mut sum = T::zero();
let p = x.shape().1; let p = x.shape().1;
for i in 0..p { for i in 0..p {
sum = sum + x.get(m_row, i) * w.get(0, i + v_col); sum += x.get(m_row, i) * w.get(0, i + v_col);
} }
sum + w.get(0, p + v_col) sum + w.get(0, p + v_col)
@@ -103,14 +103,14 @@ impl<T: RealNumber, M: Matrix<T>> PartialEq for LogisticRegression<T, M> {
|| self.num_attributes != other.num_attributes || self.num_attributes != other.num_attributes
|| self.classes.len() != other.classes.len() || self.classes.len() != other.classes.len()
{ {
return false; false
} else { } else {
for i in 0..self.classes.len() { for i in 0..self.classes.len() {
if (self.classes[i] - other.classes[i]).abs() > T::epsilon() { if (self.classes[i] - other.classes[i]).abs() > T::epsilon() {
return false; return false;
} }
} }
return self.coefficients == other.coefficients && self.intercept == other.intercept; return self.coefficients == other.coefficients && self.intercept == other.intercept;
} }
} }
@@ -125,7 +125,7 @@ impl<'a, T: RealNumber, M: Matrix<T>> ObjectiveFunction<T, M>
for i in 0..n { for i in 0..n {
let wx = BinaryObjectiveFunction::partial_dot(w_bias, self.x, 0, i); let wx = BinaryObjectiveFunction::partial_dot(w_bias, self.x, 0, i);
f = f + (wx.ln_1pe() - (T::from(self.y[i]).unwrap()) * wx); f += wx.ln_1pe() - (T::from(self.y[i]).unwrap()) * wx;
} }
f f
@@ -171,7 +171,7 @@ impl<'a, T: RealNumber, M: Matrix<T>> ObjectiveFunction<T, M>
); );
} }
prob.softmax_mut(); prob.softmax_mut();
f = f - prob.get(0, self.y[i]).ln(); f -= prob.get(0, self.y[i]).ln();
} }
f f
@@ -217,9 +217,9 @@ impl<T: RealNumber, M: Matrix<T>> LogisticRegression<T, M> {
let (_, y_nrows) = y_m.shape(); let (_, y_nrows) = y_m.shape();
if x_nrows != y_nrows { if x_nrows != y_nrows {
return Err(Failed::fit(&format!( return Err(Failed::fit(
"Number of rows of X doesn't match number of rows of Y" &"Number of rows of X doesn\'t match number of rows of Y".to_string(),
))); ));
} }
let classes = y_m.unique(); let classes = y_m.unique();
@@ -248,6 +248,7 @@ impl<T: RealNumber, M: Matrix<T>> LogisticRegression<T, M> {
}; };
let result = LogisticRegression::minimize(x0, objective); let result = LogisticRegression::minimize(x0, objective);
let weights = result.x; let weights = result.x;
Ok(LogisticRegression { Ok(LogisticRegression {
@@ -269,7 +270,6 @@ impl<T: RealNumber, M: Matrix<T>> LogisticRegression<T, M> {
}; };
let result = LogisticRegression::minimize(x0, objective); let result = LogisticRegression::minimize(x0, objective);
let weights = result.x.reshape(k, num_attributes + 1); let weights = result.x.reshape(k, num_attributes + 1);
Ok(LogisticRegression { Ok(LogisticRegression {
@@ -332,8 +332,10 @@ impl<T: RealNumber, M: Matrix<T>> LogisticRegression<T, M> {
let df = |g: &mut M, w: &M| objective.df(g, w); let df = |g: &mut M, w: &M| objective.df(g, w);
let mut ls: Backtracking<T> = Default::default(); let ls: Backtracking<T> = Backtracking {
ls.order = FunctionOrder::THIRD; order: FunctionOrder::THIRD,
..Default::default()
};
let optimizer: LBFGS<T> = Default::default(); let optimizer: LBFGS<T> = Default::default();
optimizer.optimize(&f, &df, &x0, &ls) optimizer.optimize(&f, &df, &x0, &ls)
@@ -371,7 +373,7 @@ mod tests {
let objective = MultiClassObjectiveFunction { let objective = MultiClassObjectiveFunction {
x: &x, x: &x,
y: y, y,
k: 3, k: 3,
phantom: PhantomData, phantom: PhantomData,
}; };
@@ -420,7 +422,7 @@ mod tests {
let objective = BinaryObjectiveFunction { let objective = BinaryObjectiveFunction {
x: &x, x: &x,
y: y, y,
phantom: PhantomData, phantom: PhantomData,
}; };
+1 -1
View File
@@ -38,7 +38,7 @@ impl Euclidian {
let mut sum = T::zero(); let mut sum = T::zero();
for i in 0..x.len() { for i in 0..x.len() {
let d = x[i] - y[i]; let d = x[i] - y[i];
sum = sum + d * d; sum += d * d;
} }
sum sum
+5 -5
View File
@@ -68,8 +68,8 @@ impl<T: RealNumber, M: Matrix<T>> Mahalanobis<T, M> {
let sigma = data.cov(); let sigma = data.cov();
let sigmaInv = sigma.lu().and_then(|lu| lu.inverse()).unwrap(); let sigmaInv = sigma.lu().and_then(|lu| lu.inverse()).unwrap();
Mahalanobis { Mahalanobis {
sigma: sigma, sigma,
sigmaInv: sigmaInv, sigmaInv,
t: PhantomData, t: PhantomData,
} }
} }
@@ -80,8 +80,8 @@ impl<T: RealNumber, M: Matrix<T>> Mahalanobis<T, M> {
let sigma = cov.clone(); let sigma = cov.clone();
let sigmaInv = sigma.lu().and_then(|lu| lu.inverse()).unwrap(); let sigmaInv = sigma.lu().and_then(|lu| lu.inverse()).unwrap();
Mahalanobis { Mahalanobis {
sigma: sigma, sigma,
sigmaInv: sigmaInv, sigmaInv,
t: PhantomData, t: PhantomData,
} }
} }
@@ -118,7 +118,7 @@ impl<T: RealNumber, M: Matrix<T>> Distance<Vec<T>, T> for Mahalanobis<T, M> {
let mut s = T::zero(); let mut s = T::zero();
for j in 0..n { for j in 0..n {
for i in 0..n { for i in 0..n {
s = s + self.sigmaInv.get(i, j) * z[i] * z[j]; s += self.sigmaInv.get(i, j) * z[i] * z[j];
} }
} }
+1 -1
View File
@@ -35,7 +35,7 @@ impl<T: RealNumber> Distance<Vec<T>, T> for Manhattan {
let mut dist = T::zero(); let mut dist = T::zero();
for i in 0..x.len() { for i in 0..x.len() {
dist = dist + (x[i] - y[i]).abs(); dist += (x[i] - y[i]).abs();
} }
dist dist
+1 -1
View File
@@ -48,7 +48,7 @@ impl<T: RealNumber> Distance<Vec<T>, T> for Minkowski {
for i in 0..x.len() { for i in 0..x.len() {
let d = (x[i] - y[i]).abs(); let d = (x[i] - y[i]).abs();
dist = dist + d.powf(p_t); dist += d.powf(p_t);
} }
dist.powf(T::one() / p_t) dist.powf(T::one() / p_t)
+2 -2
View File
@@ -4,7 +4,7 @@
//! Formally, the distance can be any metric measure that is defined as \\( d(x, y) \geq 0\\) and follows three conditions: //! Formally, the distance can be any metric measure that is defined as \\( d(x, y) \geq 0\\) and follows three conditions:
//! 1. \\( d(x, y) = 0 \\) if and only \\( x = y \\), positive definiteness //! 1. \\( d(x, y) = 0 \\) if and only \\( x = y \\), positive definiteness
//! 1. \\( d(x, y) = d(y, x) \\), symmetry //! 1. \\( d(x, y) = d(y, x) \\), symmetry
//! 1. \\( d(x, y) \leq d(x, z) + d(z, y) \\), subadditivity or triangle inequality //! 1. \\( d(x, y) \leq d(x, z) + d(z, y) \\), subadditivity or triangle inequality
//! //!
//! for all \\(x, y, z \in Z \\) //! for all \\(x, y, z \in Z \\)
//! //!
@@ -45,7 +45,7 @@ impl Distances {
/// Minkowski distance, see [`Minkowski`](minkowski/index.html) /// Minkowski distance, see [`Minkowski`](minkowski/index.html)
/// * `p` - function order. Should be >= 1 /// * `p` - function order. Should be >= 1
pub fn minkowski(p: u16) -> minkowski::Minkowski { pub fn minkowski(p: u16) -> minkowski::Minkowski {
minkowski::Minkowski { p: p } minkowski::Minkowski { p }
} }
/// Manhattan distance, see [`Manhattan`](manhattan/index.html) /// Manhattan distance, see [`Manhattan`](manhattan/index.html)
+10 -10
View File
@@ -57,19 +57,19 @@ impl RealNumber for f64 {
fn ln_1pe(self) -> f64 { fn ln_1pe(self) -> f64 {
if self > 15. { if self > 15. {
return self; self
} else { } else {
return self.exp().ln_1p(); self.exp().ln_1p()
} }
} }
fn sigmoid(self) -> f64 { fn sigmoid(self) -> f64 {
if self < -40. { if self < -40. {
return 0.; 0.
} else if self > 40. { } else if self > 40. {
return 1.; 1.
} else { } else {
return 1. / (1. + f64::exp(-self)); 1. / (1. + f64::exp(-self))
} }
} }
@@ -98,19 +98,19 @@ impl RealNumber for f32 {
fn ln_1pe(self) -> f32 { fn ln_1pe(self) -> f32 {
if self > 15. { if self > 15. {
return self; self
} else { } else {
return self.exp().ln_1p(); self.exp().ln_1p()
} }
} }
fn sigmoid(self) -> f32 { fn sigmoid(self) -> f32 {
if self < -40. { if self < -40. {
return 0.; 0.
} else if self > 40. { } else if self > 40. {
return 1.; 1.
} else { } else {
return 1. / (1. + f32::exp(-self)); 1. / (1. + f32::exp(-self))
} }
} }
+3 -3
View File
@@ -42,9 +42,9 @@ impl AUC {
for i in 0..n { for i in 0..n {
if y_true.get(i) == T::zero() { if y_true.get(i) == T::zero() {
neg = neg + T::one(); neg += T::one();
} else if y_true.get(i) == T::one() { } else if y_true.get(i) == T::one() {
pos = pos + T::one(); pos += T::one();
} else { } else {
panic!( panic!(
"AUC is only for binary classification. Invalid label: {}", "AUC is only for binary classification. Invalid label: {}",
@@ -79,7 +79,7 @@ impl AUC {
let mut auc = T::zero(); let mut auc = T::zero();
for i in 0..n { for i in 0..n {
if y_true.get(label_idx[i]) == T::one() { if y_true.get(label_idx[i]) == T::one() {
auc = auc + rank[i]; auc += rank[i];
} }
} }
+2 -2
View File
@@ -24,8 +24,8 @@ impl HCVScore {
let contingency = contingency_matrix(&labels_true, &labels_pred); let contingency = contingency_matrix(&labels_true, &labels_pred);
let mi: T = mutual_info_score(&contingency); let mi: T = mutual_info_score(&contingency);
let homogeneity = entropy_c.map(|e| mi / e).unwrap_or(T::one()); let homogeneity = entropy_c.map(|e| mi / e).unwrap_or_else(T::one);
let completeness = entropy_k.map(|e| mi / e).unwrap_or(T::one()); let completeness = entropy_k.map(|e| mi / e).unwrap_or_else(T::one);
let v_measure_score = if homogeneity + completeness == T::zero() { let v_measure_score = if homogeneity + completeness == T::zero() {
T::zero() T::zero()
+3 -4
View File
@@ -37,7 +37,7 @@ pub fn entropy<T: RealNumber>(data: &Vec<T>) -> Option<T> {
for &c in bincounts.values() { for &c in bincounts.values() {
if c > 0 { if c > 0 {
let pi = T::from_usize(c).unwrap(); let pi = T::from_usize(c).unwrap();
entropy = entropy - (pi / sum) * (pi.ln() - sum.ln()); entropy -= (pi / sum) * (pi.ln() - sum.ln());
} }
} }
@@ -89,9 +89,8 @@ pub fn mutual_info_score<T: RealNumber>(contingency: &Vec<Vec<usize>>) -> T {
let mut result = T::zero(); let mut result = T::zero();
for i in 0..log_outer.len() { for i in 0..log_outer.len() {
result = result result += (contingency_nm[i] * (log_contingency_nm[i] - contingency_sum_ln))
+ ((contingency_nm[i] * (log_contingency_nm[i] - contingency_sum_ln)) + contingency_nm[i] * log_outer[i]
+ contingency_nm[i] * log_outer[i])
} }
result.max(T::zero()) result.max(T::zero())
+1 -1
View File
@@ -43,7 +43,7 @@ impl MeanAbsoluteError {
let n = y_true.len(); let n = y_true.len();
let mut ras = T::zero(); let mut ras = T::zero();
for i in 0..n { for i in 0..n {
ras = ras + (y_true.get(i) - y_pred.get(i)).abs(); ras += (y_true.get(i) - y_pred.get(i)).abs();
} }
ras / T::from_usize(n).unwrap() ras / T::from_usize(n).unwrap()
+1 -1
View File
@@ -43,7 +43,7 @@ impl MeanSquareError {
let n = y_true.len(); let n = y_true.len();
let mut rss = T::zero(); let mut rss = T::zero();
for i in 0..n { for i in 0..n {
rss = rss + (y_true.get(i) - y_pred.get(i)).square(); rss += (y_true.get(i) - y_pred.get(i)).square();
} }
rss / T::from_usize(n).unwrap() rss / T::from_usize(n).unwrap()
+1 -1
View File
@@ -101,7 +101,7 @@ impl ClassificationMetrics {
/// F1 score, also known as balanced F-score or F-measure, see [F1](f1/index.html). /// F1 score, also known as balanced F-score or F-measure, see [F1](f1/index.html).
pub fn f1<T: RealNumber>(beta: T) -> f1::F1<T> { pub fn f1<T: RealNumber>(beta: T) -> f1::F1<T> {
f1::F1 { beta: beta } f1::F1 { beta }
} }
/// Area Under the Receiver Operating Characteristic Curve (ROC AUC), see [AUC](auc/index.html). /// Area Under the Receiver Operating Characteristic Curve (ROC AUC), see [AUC](auc/index.html).
+4 -4
View File
@@ -45,10 +45,10 @@ impl R2 {
let mut mean = T::zero(); let mut mean = T::zero();
for i in 0..n { for i in 0..n {
mean = mean + y_true.get(i); mean += y_true.get(i);
} }
mean = mean / T::from_usize(n).unwrap(); mean /= T::from_usize(n).unwrap();
let mut ss_tot = T::zero(); let mut ss_tot = T::zero();
let mut ss_res = T::zero(); let mut ss_res = T::zero();
@@ -56,8 +56,8 @@ impl R2 {
for i in 0..n { for i in 0..n {
let y_i = y_true.get(i); let y_i = y_true.get(i);
let f_i = y_pred.get(i); let f_i = y_pred.get(i);
ss_tot = ss_tot + (y_i - mean).square(); ss_tot += (y_i - mean).square();
ss_res = ss_res + (y_i - f_i).square(); ss_res += (y_i - f_i).square();
} }
T::one() - (ss_res / ss_tot) T::one() - (ss_res / ss_tot)
+9 -10
View File
@@ -8,7 +8,6 @@
//! your data. //! your data.
//! //!
//! In SmartCore you can split your data into training and test datasets using `train_test_split` function. //! In SmartCore you can split your data into training and test datasets using `train_test_split` function.
extern crate rand;
use crate::linalg::BaseVector; use crate::linalg::BaseVector;
use crate::linalg::Matrix; use crate::linalg::Matrix;
@@ -111,7 +110,7 @@ pub struct KFold {
impl Default for KFold { impl Default for KFold {
fn default() -> KFold { fn default() -> KFold {
KFold { KFold {
n_splits: 3 as usize, n_splits: 3_usize,
shuffle: true, shuffle: true,
} }
} }
@@ -127,7 +126,7 @@ impl BaseKFold for KFold {
// initialise indices // initialise indices
let mut indices: Vec<usize> = (0..n_samples).collect(); let mut indices: Vec<usize> = (0..n_samples).collect();
if self.shuffle == true { if self.shuffle {
indices.shuffle(&mut thread_rng()); indices.shuffle(&mut thread_rng());
} }
// return a new array of given shape n_split, filled with each element of n_samples divided by n_splits. // return a new array of given shape n_split, filled with each element of n_samples divided by n_splits.
@@ -135,7 +134,7 @@ impl BaseKFold for KFold {
// increment by one if odd // increment by one if odd
for i in 0..(n_samples % self.n_splits) { for i in 0..(n_samples % self.n_splits) {
fold_sizes[i] = fold_sizes[i] + 1; fold_sizes[i] += 1;
} }
// generate the right array of arrays for test indices // generate the right array of arrays for test indices
@@ -175,13 +174,13 @@ impl BaseKFold for KFold {
.clone() .clone()
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(idx, _)| test_index[idx] == false) .filter(|&(idx, _)| !test_index[idx])
.map(|(idx, _)| idx) .map(|(idx, _)| idx)
.collect::<Vec<usize>>(); // filter train indices out according to mask .collect::<Vec<usize>>(); // filter train indices out according to mask
let test_index = indices let test_index = indices
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(idx, _)| test_index[idx] == true) .filter(|&(idx, _)| test_index[idx])
.map(|(idx, _)| idx) .map(|(idx, _)| idx)
.collect::<Vec<usize>>(); // filter tests indices out according to mask .collect::<Vec<usize>>(); // filter tests indices out according to mask
return_values.push((train_index, test_index)) return_values.push((train_index, test_index))
@@ -293,10 +292,10 @@ mod tests {
let x: DenseMatrix<f64> = DenseMatrix::rand(23, 100); let x: DenseMatrix<f64> = DenseMatrix::rand(23, 100);
let train_test_splits = k.split(&x); let train_test_splits = k.split(&x);
assert_eq!(train_test_splits[0].1.len(), 12 as usize); assert_eq!(train_test_splits[0].1.len(), 12_usize);
assert_eq!(train_test_splits[0].0.len(), 11 as usize); assert_eq!(train_test_splits[0].0.len(), 11_usize);
assert_eq!(train_test_splits[1].0.len(), 12 as usize); assert_eq!(train_test_splits[1].0.len(), 12_usize);
assert_eq!(train_test_splits[1].1.len(), 11 as usize); assert_eq!(train_test_splits[1].1.len(), 11_usize);
} }
#[test] #[test]
+232
View File
@@ -0,0 +1,232 @@
use crate::error::Failed;
use crate::linalg::BaseVector;
use crate::linalg::Matrix;
use crate::math::num::RealNumber;
use crate::naive_bayes::{BaseNaiveBayes, NBDistribution};
use serde::{Deserialize, Serialize};
/// Naive Bayes classifier for categorical features
struct CategoricalNBDistribution<T: RealNumber> {
class_labels: Vec<T>,
class_probabilities: Vec<T>,
coef: Vec<Vec<Vec<T>>>,
feature_categories: Vec<Vec<T>>,
}
impl<T: RealNumber, M: Matrix<T>> NBDistribution<T, M> for CategoricalNBDistribution<T> {
fn prior(&self, class_index: usize) -> T {
if class_index >= self.class_labels.len() {
T::zero()
} else {
self.class_probabilities[class_index]
}
}
fn conditional_probability(&self, class_index: usize, j: &M::RowVector) -> T {
if class_index < self.class_labels.len() {
let mut prob = T::one();
for feature in 0..j.len() {
let value = j.get(feature);
match self.feature_categories[feature]
.iter()
.position(|&t| t == value)
{
Some(_i) => prob *= self.coef[class_index][feature][_i],
None => return T::zero(),
}
}
prob
} else {
T::zero()
}
}
fn classes(&self) -> &Vec<T> {
&self.class_labels
}
}
impl<T: RealNumber> CategoricalNBDistribution<T> {
/// Fits the distribution to a NxM matrix where N is number of samples and M is number of features.
/// * `x` - training data.
/// * `y` - vector with target values (classes) of length N.
/// * `alpha` - Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing).
pub fn fit<M: Matrix<T>>(x: &M, y: &M::RowVector, alpha: T) -> Result<Self, Failed> {
if alpha < T::zero() {
return Err(Failed::fit(&format!(
"alpha should be >= 0, alpha=[{}]",
alpha
)));
}
let (n_samples, n_features) = x.shape();
let y_samples = y.len();
if y_samples != n_samples {
return Err(Failed::fit(&format!(
"Size of x should equal size of y; |x|=[{}], |y|=[{}]",
n_samples, y_samples
)));
}
if n_samples == 0 {
return Err(Failed::fit(&format!(
"Size of x and y should greater than 0; |x|=[{}]",
n_samples
)));
}
let mut y_sorted = y.to_vec();
y_sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mut class_labels = Vec::with_capacity(y.len());
class_labels.push(y_sorted[0]);
let mut classes_count = Vec::with_capacity(y.len());
let mut current_count = T::one();
for idx in 1..y_samples {
if y_sorted[idx] == y_sorted[idx - 1] {
current_count += T::one();
} else {
classes_count.push(current_count);
class_labels.push(y_sorted[idx]);
current_count = T::one()
}
classes_count.push(current_count);
}
let mut feature_categories: Vec<Vec<T>> = Vec::with_capacity(n_features);
for feature in 0..n_features {
let feature_types = x.get_col_as_vec(feature).unique();
feature_categories.push(feature_types);
}
let mut coef: Vec<Vec<Vec<T>>> = Vec::with_capacity(class_labels.len());
for (label, label_count) in class_labels.iter().zip(classes_count.iter()) {
let mut coef_i: Vec<Vec<T>> = Vec::with_capacity(n_features);
for (feature_index, feature_options) in
feature_categories.iter().enumerate().take(n_features)
{
let col = x
.get_col_as_vec(feature_index)
.iter()
.enumerate()
.filter(|(i, _j)| y.get(*i) == *label)
.map(|(_, j)| *j)
.collect::<Vec<T>>();
let mut feat_count: Vec<usize> = Vec::with_capacity(feature_options.len());
for k in feature_options.iter() {
let feat_k_count = col.iter().filter(|&v| v == k).count();
feat_count.push(feat_k_count);
}
let coef_i_j = feat_count
.iter()
.map(|c| {
(T::from(*c).unwrap() + alpha)
/ (T::from(*label_count).unwrap()
+ T::from(feature_options.len()).unwrap() * alpha)
})
.collect::<Vec<T>>();
coef_i.push(coef_i_j);
}
coef.push(coef_i);
}
let class_probabilities = classes_count
.into_iter()
.map(|count| count / T::from(n_samples).unwrap())
.collect::<Vec<T>>();
Ok(Self {
class_labels,
class_probabilities,
coef,
feature_categories,
})
}
}
/// `CategoricalNB` parameters. Use `Default::default()` for default values.
#[derive(Serialize, Deserialize, Debug)]
pub struct CategoricalNBParameters<T: RealNumber> {
/// Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing).
pub alpha: T,
}
impl<T: RealNumber> CategoricalNBParameters<T> {
/// Create CategoricalNBParameters with specific paramaters.
pub fn new(alpha: T) -> Result<Self, Failed> {
if alpha > T::zero() {
Ok(Self { alpha })
} else {
Err(Failed::fit(&format!(
"alpha should be >= 0, alpha=[{}]",
alpha
)))
}
}
}
impl<T: RealNumber> Default for CategoricalNBParameters<T> {
fn default() -> Self {
Self { alpha: T::one() }
}
}
/// CategoricalNB implements the categorical naive Bayes algorithm for categorically distributed data.
pub struct CategoricalNB<T: RealNumber, M: Matrix<T>> {
inner: BaseNaiveBayes<T, M, CategoricalNBDistribution<T>>,
}
impl<T: RealNumber, M: Matrix<T>> CategoricalNB<T, M> {
/// Fits CategoricalNB with given data
/// * `x` - training data of size NxM where N is the number of samples and M is the number of
/// features.
/// * `y` - vector with target values (classes) of length N.
/// * `parameters` - additional parameters like alpha for smoothing
pub fn fit(
x: &M,
y: &M::RowVector,
parameters: CategoricalNBParameters<T>,
) -> Result<Self, Failed> {
let alpha = parameters.alpha;
let distribution = CategoricalNBDistribution::fit(x, y, alpha)?;
let inner = BaseNaiveBayes::fit(distribution)?;
Ok(Self { inner })
}
/// Estimates the class labels for the provided data.
/// * `x` - data of shape NxM where N is number of data points to estimate and M is number of features.
/// Returns a vector of size N with class estimates.
pub fn predict(&self, x: &M) -> Result<M::RowVector, Failed> {
self.inner.predict(x)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::linalg::naive::dense_matrix::DenseMatrix;
#[test]
fn run_base_naive_bayes() {
let x = DenseMatrix::from_2d_array(&[
&[0., 2., 1., 0.],
&[0., 2., 1., 1.],
&[1., 2., 1., 0.],
&[2., 1., 1., 0.],
&[2., 0., 0., 0.],
&[2., 0., 0., 1.],
&[1., 0., 0., 1.],
&[0., 1., 1., 0.],
&[0., 0., 0., 0.],
&[2., 1., 0., 0.],
&[0., 1., 0., 1.],
&[1., 1., 1., 1.],
&[1., 2., 0., 0.],
&[2., 1., 1., 1.],
]);
let y = vec![0., 0., 1., 1., 1., 0., 1., 0., 1., 1., 1., 1., 1., 0.];
let cnb = CategoricalNB::fit(&x, &y, Default::default()).unwrap();
let x_test = DenseMatrix::from_2d_array(&[&[0., 2., 1., 0.], &[2., 2., 0., 0.]]);
let y_hat = cnb.predict(&x_test).unwrap();
assert_eq!(y_hat, vec![0., 1.]);
}
}
+69
View File
@@ -0,0 +1,69 @@
use crate::error::Failed;
use crate::linalg::BaseVector;
use crate::linalg::Matrix;
use crate::math::num::RealNumber;
use std::marker::PhantomData;
/// Distribution used in the Naive Bayes classifier.
pub(crate) trait NBDistribution<T: RealNumber, M: Matrix<T>> {
/// Prior of class at the given index.
fn prior(&self, class_index: usize) -> T;
/// Conditional probability of sample j given class in the specified index.
fn conditional_probability(&self, class_index: usize, j: &M::RowVector) -> T;
/// Possible classes of the distribution.
fn classes(&self) -> &Vec<T>;
}
/// Base struct for the Naive Bayes classifier.
pub(crate) struct BaseNaiveBayes<T: RealNumber, M: Matrix<T>, D: NBDistribution<T, M>> {
distribution: D,
_phantom_t: PhantomData<T>,
_phantom_m: PhantomData<M>,
}
impl<T: RealNumber, M: Matrix<T>, D: NBDistribution<T, M>> BaseNaiveBayes<T, M, D> {
/// Fits NB classifier to a given NBdistribution.
/// * `distribution` - NBDistribution of the training data
pub fn fit(distribution: D) -> Result<Self, Failed> {
Ok(Self {
distribution,
_phantom_t: PhantomData,
_phantom_m: PhantomData,
})
}
/// Estimates the class labels for the provided data.
/// * `x` - data of shape NxM where N is number of data points to estimate and M is number of features.
/// Returns a vector of size N with class estimates.
pub fn predict(&self, x: &M) -> Result<M::RowVector, Failed> {
let y_classes = self.distribution.classes();
let (rows, _) = x.shape();
let predictions = (0..rows)
.map(|row_index| {
let row = x.get_row(row_index);
let (prediction, _probability) = y_classes
.iter()
.enumerate()
.map(|(class_index, class)| {
(
class,
self.distribution.conditional_probability(class_index, &row)
* self.distribution.prior(class_index),
)
})
.max_by(|(_, p1), (_, p2)| p1.partial_cmp(p2).unwrap())
.unwrap();
*prediction
})
.collect::<Vec<T>>();
let mut y_hat = M::RowVector::zeros(rows);
for (i, prediction) in predictions.iter().enumerate().take(rows) {
y_hat.set(i, *prediction);
}
Ok(y_hat)
}
}
mod categorical;
pub use categorical::{CategoricalNB, CategoricalNBParameters};
+4 -4
View File
@@ -78,7 +78,7 @@ impl<T: RealNumber, D: Distance<Vec<T>, T>> PartialEq for KNNClassifier<T, D> {
|| self.k != other.k || self.k != other.k
|| self.y.len() != other.y.len() || self.y.len() != other.y.len()
{ {
return false; false
} else { } else {
for i in 0..self.classes.len() { for i in 0..self.classes.len() {
if (self.classes[i] - other.classes[i]).abs() > T::epsilon() { if (self.classes[i] - other.classes[i]).abs() > T::epsilon() {
@@ -139,7 +139,7 @@ impl<T: RealNumber, D: Distance<Vec<T>, T>> KNNClassifier<T, D> {
} }
Ok(KNNClassifier { Ok(KNNClassifier {
classes: classes, classes,
y: yi, y: yi,
k: parameters.k, k: parameters.k,
knn_algorithm: parameters.algorithm.fit(data, distance)?, knn_algorithm: parameters.algorithm.fit(data, distance)?,
@@ -166,13 +166,13 @@ impl<T: RealNumber, D: Distance<Vec<T>, T>> KNNClassifier<T, D> {
let weights = self let weights = self
.weight .weight
.calc_weights(search_result.iter().map(|v| v.1).collect()); .calc_weights(search_result.iter().map(|v| v.1).collect());
let w_sum = weights.iter().map(|w| *w).sum(); let w_sum = weights.iter().copied().sum();
let mut c = vec![T::zero(); self.classes.len()]; let mut c = vec![T::zero(); self.classes.len()];
let mut max_c = T::zero(); let mut max_c = T::zero();
let mut max_i = 0; let mut max_i = 0;
for (r, w) in search_result.iter().zip(weights.iter()) { for (r, w) in search_result.iter().zip(weights.iter()) {
c[self.y[r.0]] = c[self.y[r.0]] + (*w / w_sum); c[self.y[r.0]] += *w / w_sum;
if c[self.y[r.0]] > max_c { if c[self.y[r.0]] > max_c {
max_c = c[self.y[r.0]]; max_c = c[self.y[r.0]];
max_i = self.y[r.0]; max_i = self.y[r.0];
+3 -3
View File
@@ -76,7 +76,7 @@ impl Default for KNNRegressorParameters {
impl<T: RealNumber, D: Distance<Vec<T>, T>> PartialEq for KNNRegressor<T, D> { impl<T: RealNumber, D: Distance<Vec<T>, T>> PartialEq for KNNRegressor<T, D> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.k != other.k || self.y.len() != other.y.len() { if self.k != other.k || self.y.len() != other.y.len() {
return false; false
} else { } else {
for i in 0..self.y.len() { for i in 0..self.y.len() {
if (self.y[i] - other.y[i]).abs() > T::epsilon() { if (self.y[i] - other.y[i]).abs() > T::epsilon() {
@@ -151,10 +151,10 @@ impl<T: RealNumber, D: Distance<Vec<T>, T>> KNNRegressor<T, D> {
let weights = self let weights = self
.weight .weight
.calc_weights(search_result.iter().map(|v| v.1).collect()); .calc_weights(search_result.iter().map(|v| v.1).collect());
let w_sum = weights.iter().map(|w| *w).sum(); let w_sum = weights.iter().copied().sum();
for (r, w) in search_result.iter().zip(weights.iter()) { for (r, w) in search_result.iter().zip(weights.iter()) {
result = result + self.y[r.0] * (*w / w_sum); result += self.y[r.0] * (*w / w_sum);
} }
Ok(result) Ok(result)
+1 -1
View File
@@ -10,7 +10,7 @@
//! and follows three conditions: //! and follows three conditions:
//! 1. \\( d(x, y) = 0 \\) if and only \\( x = y \\), positive definiteness //! 1. \\( d(x, y) = 0 \\) if and only \\( x = y \\), positive definiteness
//! 1. \\( d(x, y) = d(y, x) \\), symmetry //! 1. \\( d(x, y) = d(y, x) \\), symmetry
//! 1. \\( d(x, y) \leq d(x, z) + d(z, y) \\), subadditivity or triangle inequality //! 1. \\( d(x, y) \leq d(x, z) + d(z, y) \\), subadditivity or triangle inequality
//! //!
//! for all \\(x, y, z \in Z \\) //! for all \\(x, y, z \in Z \\)
//! //!
@@ -25,8 +25,8 @@ impl<T: RealNumber> Default for GradientDescent<T> {
impl<T: RealNumber> FirstOrderOptimizer<T> for GradientDescent<T> { impl<T: RealNumber> FirstOrderOptimizer<T> for GradientDescent<T> {
fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>( fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>(
&self, &self,
f: &'a F<T, X>, f: &'a F<'_, T, X>,
df: &'a DF<X>, df: &'a DF<'_, X>,
x0: &X, x0: &X,
ls: &'a LS, ls: &'a LS,
) -> OptimizerResult<T, X> { ) -> OptimizerResult<T, X> {
@@ -74,8 +74,8 @@ impl<T: RealNumber> FirstOrderOptimizer<T> for GradientDescent<T> {
let f_x = f(&x); let f_x = f(&x);
OptimizerResult { OptimizerResult {
x: x, x,
f_x: f_x, f_x,
iterations: iter, iterations: iter,
} }
} }
+5 -5
View File
@@ -100,8 +100,8 @@ impl<T: RealNumber> LBFGS<T> {
fn update_state<'a, X: Matrix<T>, LS: LineSearchMethod<T>>( fn update_state<'a, X: Matrix<T>, LS: LineSearchMethod<T>>(
&self, &self,
f: &'a F<T, X>, f: &'a F<'_, T, X>,
df: &'a DF<X>, df: &'a DF<'_, X>,
ls: &'a LS, ls: &'a LS,
state: &mut LBFGSState<T, X>, state: &mut LBFGSState<T, X>,
) { ) {
@@ -162,7 +162,7 @@ impl<T: RealNumber> LBFGS<T> {
g_converged || x_converged || state.counter_f_tol > self.successive_f_tol g_converged || x_converged || state.counter_f_tol > self.successive_f_tol
} }
fn update_hessian<'a, X: Matrix<T>>(&self, _: &'a DF<X>, state: &mut LBFGSState<T, X>) { fn update_hessian<'a, X: Matrix<T>>(&self, _: &'a DF<'_, X>, state: &mut LBFGSState<T, X>) {
state.dg = state.x_df.sub(&state.x_df_prev); state.dg = state.x_df.sub(&state.x_df_prev);
let rho_iteration = T::one() / state.dx.dot(&state.dg); let rho_iteration = T::one() / state.dx.dot(&state.dg);
if !rho_iteration.is_infinite() { if !rho_iteration.is_infinite() {
@@ -198,8 +198,8 @@ struct LBFGSState<T: RealNumber, X: Matrix<T>> {
impl<T: RealNumber> FirstOrderOptimizer<T> for LBFGS<T> { impl<T: RealNumber> FirstOrderOptimizer<T> for LBFGS<T> {
fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>( fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>(
&self, &self,
f: &F<T, X>, f: &F<'_, T, X>,
df: &'a DF<X>, df: &'a DF<'_, X>,
x0: &X, x0: &X,
ls: &'a LS, ls: &'a LS,
) -> OptimizerResult<T, X> { ) -> OptimizerResult<T, X> {
+2 -2
View File
@@ -12,8 +12,8 @@ use crate::optimization::{DF, F};
pub trait FirstOrderOptimizer<T: RealNumber> { pub trait FirstOrderOptimizer<T: RealNumber> {
fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>( fn optimize<'a, X: Matrix<T>, LS: LineSearchMethod<T>>(
&self, &self,
f: &F<T, X>, f: &F<'_, T, X>,
df: &'a DF<X>, df: &'a DF<'_, X>,
x0: &X, x0: &X,
ls: &'a LS, ls: &'a LS,
) -> OptimizerResult<T, X>; ) -> OptimizerResult<T, X>;
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::optimization::FunctionOrder;
use num_traits::Float; use num_traits::Float;
pub trait LineSearchMethod<T: Float> { pub trait LineSearchMethod<T: Float> {
fn search<'a>( fn search(
&self, &self,
f: &(dyn Fn(T) -> T), f: &(dyn Fn(T) -> T),
df: &(dyn Fn(T) -> T), df: &(dyn Fn(T) -> T),
+6 -9
View File
@@ -48,7 +48,7 @@ impl Kernels {
/// Radial basis function kernel (Gaussian) /// Radial basis function kernel (Gaussian)
pub fn rbf<T: RealNumber>(gamma: T) -> RBFKernel<T> { pub fn rbf<T: RealNumber>(gamma: T) -> RBFKernel<T> {
RBFKernel { gamma: gamma } RBFKernel { gamma }
} }
/// Polynomial kernel /// Polynomial kernel
@@ -57,9 +57,9 @@ impl Kernels {
/// * `coef0` - independent term in kernel function /// * `coef0` - independent term in kernel function
pub fn polynomial<T: RealNumber>(degree: T, gamma: T, coef0: T) -> PolynomialKernel<T> { pub fn polynomial<T: RealNumber>(degree: T, gamma: T, coef0: T) -> PolynomialKernel<T> {
PolynomialKernel { PolynomialKernel {
degree: degree, degree,
gamma: gamma, gamma,
coef0: coef0, coef0,
} }
} }
@@ -79,17 +79,14 @@ impl Kernels {
/// * `gamma` - kernel coefficient /// * `gamma` - kernel coefficient
/// * `coef0` - independent term in kernel function /// * `coef0` - independent term in kernel function
pub fn sigmoid<T: RealNumber>(gamma: T, coef0: T) -> SigmoidKernel<T> { pub fn sigmoid<T: RealNumber>(gamma: T, coef0: T) -> SigmoidKernel<T> {
SigmoidKernel { SigmoidKernel { gamma, coef0 }
gamma: gamma,
coef0: coef0,
}
} }
/// Sigmoid kernel /// Sigmoid kernel
/// * `gamma` - kernel coefficient /// * `gamma` - kernel coefficient
pub fn sigmoid_with_gamma<T: RealNumber>(gamma: T) -> SigmoidKernel<T> { pub fn sigmoid_with_gamma<T: RealNumber>(gamma: T) -> SigmoidKernel<T> {
SigmoidKernel { SigmoidKernel {
gamma: gamma, gamma,
coef0: T::one(), coef0: T::one(),
} }
} }
+51 -46
View File
@@ -173,9 +173,9 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> SVC<T, M, K> {
let (n, _) = x.shape(); let (n, _) = x.shape();
if n != y.len() { if n != y.len() {
return Err(Failed::fit(&format!( return Err(Failed::fit(
"Number of rows of X doesn't match number of rows of Y" &"Number of rows of X doesn\'t match number of rows of Y".to_string(),
))); ));
} }
let classes = y.unique(); let classes = y.unique();
@@ -204,11 +204,11 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> SVC<T, M, K> {
let (support_vectors, weight, b) = optimizer.optimize(); let (support_vectors, weight, b) = optimizer.optimize();
Ok(SVC { Ok(SVC {
classes: classes, classes,
kernel: kernel, kernel,
instances: support_vectors, instances: support_vectors,
w: weight, w: weight,
b: b, b,
}) })
} }
@@ -251,7 +251,7 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> PartialEq for SVC<
|| self.w.len() != other.w.len() || self.w.len() != other.w.len()
|| self.instances.len() != other.instances.len() || self.instances.len() != other.instances.len()
{ {
return false; false
} else { } else {
for i in 0..self.w.len() { for i in 0..self.w.len() {
if (self.w[i] - other.w[i]).abs() > T::epsilon() { if (self.w[i] - other.w[i]).abs() > T::epsilon() {
@@ -263,7 +263,7 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> PartialEq for SVC<
return false; return false;
} }
} }
return true; true
} }
} }
} }
@@ -278,12 +278,12 @@ impl<T: RealNumber, V: BaseVector<T>> SupportVector<T, V> {
}; };
SupportVector { SupportVector {
index: i, index: i,
x: x, x,
grad: g, grad: g,
k: k_v, k: k_v,
alpha: T::zero(), alpha: T::zero(),
cmin: cmin, cmin,
cmax: cmax, cmax,
} }
} }
} }
@@ -291,7 +291,7 @@ impl<T: RealNumber, V: BaseVector<T>> SupportVector<T, V> {
impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Cache<'a, T, M, K> { impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Cache<'a, T, M, K> {
fn new(kernel: &'a K) -> Cache<'a, T, M, K> { fn new(kernel: &'a K) -> Cache<'a, T, M, K> {
Cache { Cache {
kernel: kernel, kernel,
data: HashMap::new(), data: HashMap::new(),
phantom: PhantomData, phantom: PhantomData,
} }
@@ -300,11 +300,12 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Cache<'a, T, M
fn get(&mut self, i: &SupportVector<T, M::RowVector>, j: &SupportVector<T, M::RowVector>) -> T { fn get(&mut self, i: &SupportVector<T, M::RowVector>, j: &SupportVector<T, M::RowVector>) -> T {
let idx_i = i.index; let idx_i = i.index;
let idx_j = j.index; let idx_j = j.index;
if !self.data.contains_key(&(idx_i, idx_j)) { #[allow(clippy::or_fun_call)]
let v = self.kernel.apply(&i.x, &j.x); let entry = self
self.data.insert((idx_i, idx_j), v); .data
} .entry((idx_i, idx_j))
*self.data.get(&(idx_i, idx_j)).unwrap() .or_insert(self.kernel.apply(&i.x, &j.x));
*entry
} }
fn insert(&mut self, key: (usize, usize), value: T) { fn insert(&mut self, key: (usize, usize), value: T) {
@@ -326,8 +327,8 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
let (n, _) = x.shape(); let (n, _) = x.shape();
Optimizer { Optimizer {
x: x, x,
y: y, y,
parameters: &parameters, parameters: &parameters,
svmin: 0, svmin: 0,
svmax: 0, svmax: 0,
@@ -335,7 +336,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
gmax: T::min_value(), gmax: T::min_value(),
tau: T::from_f64(1e-12).unwrap(), tau: T::from_f64(1e-12).unwrap(),
sv: Vec::with_capacity(n), sv: Vec::with_capacity(n),
kernel: kernel, kernel,
recalculate_minmax_grad: true, recalculate_minmax_grad: true,
} }
} }
@@ -378,7 +379,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
(support_vectors, w, b) (support_vectors, w, b)
} }
fn initialize(&mut self, cache: &mut Cache<T, M, K>) { fn initialize(&mut self, cache: &mut Cache<'_, T, M, K>) {
let (n, _) = self.x.shape(); let (n, _) = self.x.shape();
let few = 5; let few = 5;
let mut cp = 0; let mut cp = 0;
@@ -389,10 +390,11 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
if self.process(i, self.x.get_row(i), self.y.get(i), cache) { if self.process(i, self.x.get_row(i), self.y.get(i), cache) {
cp += 1; cp += 1;
} }
} else if self.y.get(i) == -T::one() && cn < few { } else if self.y.get(i) == -T::one()
if self.process(i, self.x.get_row(i), self.y.get(i), cache) { && cn < few
cn += 1; && self.process(i, self.x.get_row(i), self.y.get(i), cache)
} {
cn += 1;
} }
if cp >= few && cn >= few { if cp >= few && cn >= few {
@@ -401,7 +403,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
} }
} }
fn process(&mut self, i: usize, x: M::RowVector, y: T, cache: &mut Cache<T, M, K>) -> bool { fn process(&mut self, i: usize, x: M::RowVector, y: T, cache: &mut Cache<'_, T, M, K>) -> bool {
for j in 0..self.sv.len() { for j in 0..self.sv.len() {
if self.sv[j].index == i { if self.sv[j].index == i {
return true; return true;
@@ -420,10 +422,10 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.find_min_max_gradient(); self.find_min_max_gradient();
if self.gmin < self.gmax { if self.gmin < self.gmax
if (y > T::zero() && g < self.gmin) || (y < T::zero() && g > self.gmax) { && ((y > T::zero() && g < self.gmin) || (y < T::zero() && g > self.gmax))
return false; {
} return false;
} }
for v in cache_values { for v in cache_values {
@@ -444,13 +446,13 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
true true
} }
fn reprocess(&mut self, tol: T, cache: &mut Cache<T, M, K>) -> bool { fn reprocess(&mut self, tol: T, cache: &mut Cache<'_, T, M, K>) -> bool {
let status = self.smo(None, None, tol, cache); let status = self.smo(None, None, tol, cache);
self.clean(cache); self.clean(cache);
status status
} }
fn finish(&mut self, cache: &mut Cache<T, M, K>) { fn finish(&mut self, cache: &mut Cache<'_, T, M, K>) {
let mut max_iter = self.sv.len(); let mut max_iter = self.sv.len();
while self.smo(None, None, self.parameters.tol, cache) && max_iter > 0 { while self.smo(None, None, self.parameters.tol, cache) && max_iter > 0 {
@@ -485,7 +487,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.recalculate_minmax_grad = false self.recalculate_minmax_grad = false
} }
fn clean(&mut self, cache: &mut Cache<T, M, K>) { fn clean(&mut self, cache: &mut Cache<'_, T, M, K>) {
self.find_min_max_gradient(); self.find_min_max_gradient();
let gmax = self.gmax; let gmax = self.gmax;
@@ -494,13 +496,12 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
let mut idxs_to_drop: HashSet<usize> = HashSet::new(); let mut idxs_to_drop: HashSet<usize> = HashSet::new();
self.sv.retain(|v| { self.sv.retain(|v| {
if v.alpha == T::zero() { if v.alpha == T::zero()
if (v.grad >= gmax && T::zero() >= v.cmax) && ((v.grad >= gmax && T::zero() >= v.cmax)
|| (v.grad <= gmin && T::zero() <= v.cmin) || (v.grad <= gmin && T::zero() <= v.cmin))
{ {
idxs_to_drop.insert(v.index); idxs_to_drop.insert(v.index);
return false; return false;
}
}; };
true true
}); });
@@ -520,7 +521,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
&mut self, &mut self,
idx_1: Option<usize>, idx_1: Option<usize>,
idx_2: Option<usize>, idx_2: Option<usize>,
cache: &mut Cache<T, M, K>, cache: &mut Cache<'_, T, M, K>,
) -> Option<(usize, usize, T)> { ) -> Option<(usize, usize, T)> {
match (idx_1, idx_2) { match (idx_1, idx_2) {
(None, None) => { (None, None) => {
@@ -561,7 +562,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
( (
idx_1, idx_1,
idx_2, idx_2,
k_v_12.unwrap_or(self.kernel.apply(&self.sv[idx_1].x, &self.sv[idx_2].x)), k_v_12.unwrap_or_else(|| {
self.kernel.apply(&self.sv[idx_1].x, &self.sv[idx_2].x)
}),
) )
}) })
} }
@@ -597,7 +600,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
( (
idx_1, idx_1,
idx_2, idx_2,
k_v_12.unwrap_or(self.kernel.apply(&self.sv[idx_1].x, &self.sv[idx_2].x)), k_v_12.unwrap_or_else(|| {
self.kernel.apply(&self.sv[idx_1].x, &self.sv[idx_2].x)
}),
) )
}) })
} }
@@ -614,7 +619,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
idx_1: Option<usize>, idx_1: Option<usize>,
idx_2: Option<usize>, idx_2: Option<usize>,
tol: T, tol: T,
cache: &mut Cache<T, M, K>, cache: &mut Cache<'_, T, M, K>,
) -> bool { ) -> bool {
match self.select_pair(idx_1, idx_2, cache) { match self.select_pair(idx_1, idx_2, cache) {
Some((idx_1, idx_2, k_v_12)) => { Some((idx_1, idx_2, k_v_12)) => {
@@ -647,13 +652,13 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.update(idx_1, idx_2, step, cache); self.update(idx_1, idx_2, step, cache);
return self.gmax - self.gmin > tol; self.gmax - self.gmin > tol
} }
None => false, None => false,
} }
} }
fn update(&mut self, v1: usize, v2: usize, step: T, cache: &mut Cache<T, M, K>) { fn update(&mut self, v1: usize, v2: usize, step: T, cache: &mut Cache<'_, T, M, K>) {
self.sv[v1].alpha -= step; self.sv[v1].alpha -= step;
self.sv[v2].alpha += step; self.sv[v2].alpha += step;
+23 -31
View File
@@ -160,9 +160,9 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> SVR<T, M, K> {
let (n, _) = x.shape(); let (n, _) = x.shape();
if n != y.len() { if n != y.len() {
return Err(Failed::fit(&format!( return Err(Failed::fit(
"Number of rows of X doesn't match number of rows of Y" &"Number of rows of X doesn\'t match number of rows of Y".to_string(),
))); ));
} }
let optimizer = Optimizer::new(x, y, &kernel, &parameters); let optimizer = Optimizer::new(x, y, &kernel, &parameters);
@@ -170,10 +170,10 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> SVR<T, M, K> {
let (support_vectors, weight, b) = optimizer.smo(); let (support_vectors, weight, b) = optimizer.smo();
Ok(SVR { Ok(SVR {
kernel: kernel, kernel,
instances: support_vectors, instances: support_vectors,
w: weight, w: weight,
b: b, b,
}) })
} }
@@ -198,7 +198,7 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> SVR<T, M, K> {
f += self.w[i] * self.kernel.apply(&x, &self.instances[i]); f += self.w[i] * self.kernel.apply(&x, &self.instances[i]);
} }
return f; f
} }
} }
@@ -208,7 +208,7 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> PartialEq for SVR<
|| self.w.len() != other.w.len() || self.w.len() != other.w.len()
|| self.instances.len() != other.instances.len() || self.instances.len() != other.instances.len()
{ {
return false; false
} else { } else {
for i in 0..self.w.len() { for i in 0..self.w.len() {
if (self.w[i] - other.w[i]).abs() > T::epsilon() { if (self.w[i] - other.w[i]).abs() > T::epsilon() {
@@ -220,7 +220,7 @@ impl<T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> PartialEq for SVR<
return false; return false;
} }
} }
return true; true
} }
} }
} }
@@ -230,7 +230,7 @@ impl<T: RealNumber, V: BaseVector<T>> SupportVector<T, V> {
let k_v = k.apply(&x, &x); let k_v = k.apply(&x, &x);
SupportVector { SupportVector {
index: i, index: i,
x: x, x,
grad: [eps + y, eps - y], grad: [eps + y, eps - y],
k: k_v, k: k_v,
alpha: [T::zero(), T::zero()], alpha: [T::zero(), T::zero()],
@@ -270,7 +270,7 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
gmaxindex: 0, gmaxindex: 0,
tau: T::from_f64(1e-12).unwrap(), tau: T::from_f64(1e-12).unwrap(),
sv: support_vectors, sv: support_vectors,
kernel: kernel, kernel,
} }
} }
@@ -392,11 +392,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.sv[v2].alpha[j] = T::zero(); self.sv[v2].alpha[j] = T::zero();
self.sv[v1].alpha[i] = diff; self.sv[v1].alpha[i] = diff;
} }
} else { } else if self.sv[v1].alpha[i] < T::zero() {
if self.sv[v1].alpha[i] < T::zero() { self.sv[v1].alpha[i] = T::zero();
self.sv[v1].alpha[i] = T::zero(); self.sv[v2].alpha[j] = -diff;
self.sv[v2].alpha[j] = -diff;
}
} }
if diff > T::zero() { if diff > T::zero() {
@@ -404,11 +402,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.sv[v1].alpha[i] = self.c; self.sv[v1].alpha[i] = self.c;
self.sv[v2].alpha[j] = self.c - diff; self.sv[v2].alpha[j] = self.c - diff;
} }
} else { } else if self.sv[v2].alpha[j] > self.c {
if self.sv[v2].alpha[j] > self.c { self.sv[v2].alpha[j] = self.c;
self.sv[v2].alpha[j] = self.c; self.sv[v1].alpha[i] = self.c + diff;
self.sv[v1].alpha[i] = self.c + diff;
}
} }
} else { } else {
let delta = (self.sv[v1].grad[i] - self.sv[v2].grad[j]) / curv; let delta = (self.sv[v1].grad[i] - self.sv[v2].grad[j]) / curv;
@@ -421,11 +417,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.sv[v1].alpha[i] = self.c; self.sv[v1].alpha[i] = self.c;
self.sv[v2].alpha[j] = sum - self.c; self.sv[v2].alpha[j] = sum - self.c;
} }
} else { } else if self.sv[v2].alpha[j] < T::zero() {
if self.sv[v2].alpha[j] < T::zero() { self.sv[v2].alpha[j] = T::zero();
self.sv[v2].alpha[j] = T::zero(); self.sv[v1].alpha[i] = sum;
self.sv[v1].alpha[i] = sum;
}
} }
if sum > self.c { if sum > self.c {
@@ -433,11 +427,9 @@ impl<'a, T: RealNumber, M: Matrix<T>, K: Kernel<T, M::RowVector>> Optimizer<'a,
self.sv[v2].alpha[j] = self.c; self.sv[v2].alpha[j] = self.c;
self.sv[v1].alpha[i] = sum - self.c; self.sv[v1].alpha[i] = sum - self.c;
} }
} else { } else if self.sv[v1].alpha[i] < T::zero() {
if self.sv[v1].alpha[i] < T::zero() { self.sv[v1].alpha[i] = T::zero();
self.sv[v1].alpha[i] = T::zero(); self.sv[v2].alpha[j] = sum;
self.sv[v2].alpha[j] = sum;
}
} }
} }
@@ -477,7 +469,7 @@ impl<T: Clone> Cache<T> {
} }
} }
fn get<F: Fn() -> Vec<T>>(&self, i: usize, or: F) -> Ref<Vec<T>> { fn get<F: Fn() -> Vec<T>>(&self, i: usize, or: F) -> Ref<'_, Vec<T>> {
if self.data[i].borrow().is_none() { if self.data[i].borrow().is_none() {
self.data[i].replace(Some(or())); self.data[i].replace(Some(or()));
} }
+26 -26
View File
@@ -126,7 +126,7 @@ impl<T: RealNumber> PartialEq for DecisionTreeClassifier<T> {
|| self.num_classes != other.num_classes || self.num_classes != other.num_classes
|| self.nodes.len() != other.nodes.len() || self.nodes.len() != other.nodes.len()
{ {
return false; false
} else { } else {
for i in 0..self.classes.len() { for i in 0..self.classes.len() {
if (self.classes[i] - other.classes[i]).abs() > T::epsilon() { if (self.classes[i] - other.classes[i]).abs() > T::epsilon() {
@@ -138,7 +138,7 @@ impl<T: RealNumber> PartialEq for DecisionTreeClassifier<T> {
return false; return false;
} }
} }
return true; true
} }
} }
} }
@@ -174,8 +174,8 @@ impl Default for DecisionTreeClassifierParameters {
impl<T: RealNumber> Node<T> { impl<T: RealNumber> Node<T> {
fn new(index: usize, output: usize) -> Self { fn new(index: usize, output: usize) -> Self {
Node { Node {
index: index, index,
output: output, output,
split_feature: 0, split_feature: 0,
split_value: Option::None, split_value: Option::None,
split_score: Option::None, split_score: Option::None,
@@ -206,7 +206,7 @@ fn impurity<T: RealNumber>(criterion: &SplitCriterion, count: &Vec<usize>, n: us
for i in 0..count.len() { for i in 0..count.len() {
if count[i] > 0 { if count[i] > 0 {
let p = T::from(count[i]).unwrap() / T::from(n).unwrap(); let p = T::from(count[i]).unwrap() / T::from(n).unwrap();
impurity = impurity - p * p; impurity -= p * p;
} }
} }
} }
@@ -215,7 +215,7 @@ fn impurity<T: RealNumber>(criterion: &SplitCriterion, count: &Vec<usize>, n: us
for i in 0..count.len() { for i in 0..count.len() {
if count[i] > 0 { if count[i] > 0 {
let p = T::from(count[i]).unwrap() / T::from(n).unwrap(); let p = T::from(count[i]).unwrap() / T::from(n).unwrap();
impurity = impurity - p * p.log2(); impurity -= p * p.log2();
} }
} }
} }
@@ -229,7 +229,7 @@ fn impurity<T: RealNumber>(criterion: &SplitCriterion, count: &Vec<usize>, n: us
} }
} }
return impurity; impurity
} }
impl<'a, T: RealNumber, M: Matrix<T>> NodeVisitor<'a, T, M> { impl<'a, T: RealNumber, M: Matrix<T>> NodeVisitor<'a, T, M> {
@@ -242,14 +242,14 @@ impl<'a, T: RealNumber, M: Matrix<T>> NodeVisitor<'a, T, M> {
level: u16, level: u16,
) -> Self { ) -> Self {
NodeVisitor { NodeVisitor {
x: x, x,
y: y, y,
node: node_id, node: node_id,
samples: samples, samples,
order: order, order,
true_child_output: 0, true_child_output: 0,
false_child_output: 0, false_child_output: 0,
level: level, level,
phantom: PhantomData, phantom: PhantomData,
} }
} }
@@ -266,7 +266,7 @@ pub(in crate) fn which_max(x: &Vec<usize>) -> usize {
} }
} }
return which; which
} }
impl<T: RealNumber> DecisionTreeClassifier<T> { impl<T: RealNumber> DecisionTreeClassifier<T> {
@@ -325,16 +325,16 @@ impl<T: RealNumber> DecisionTreeClassifier<T> {
} }
let mut tree = DecisionTreeClassifier { let mut tree = DecisionTreeClassifier {
nodes: nodes, nodes,
parameters: parameters, parameters,
num_classes: k, num_classes: k,
classes: classes, classes,
depth: 0, depth: 0,
}; };
let mut visitor = NodeVisitor::<T, M>::new(0, samples, &order, &x, &yi, 1); let mut visitor = NodeVisitor::<T, M>::new(0, samples, &order, &x, &yi, 1);
let mut visitor_queue: LinkedList<NodeVisitor<T, M>> = LinkedList::new(); let mut visitor_queue: LinkedList<NodeVisitor<'_, T, M>> = LinkedList::new();
if tree.find_best_cutoff(&mut visitor, mtry) { if tree.find_best_cutoff(&mut visitor, mtry) {
visitor_queue.push_back(visitor); visitor_queue.push_back(visitor);
@@ -376,24 +376,24 @@ impl<T: RealNumber> DecisionTreeClassifier<T> {
let node = &self.nodes[node_id]; let node = &self.nodes[node_id];
if node.true_child == None && node.false_child == None { if node.true_child == None && node.false_child == None {
result = node.output; result = node.output;
} else if x.get(row, node.split_feature)
<= node.split_value.unwrap_or_else(T::nan)
{
queue.push_back(node.true_child.unwrap());
} else { } else {
if x.get(row, node.split_feature) <= node.split_value.unwrap_or(T::nan()) { queue.push_back(node.false_child.unwrap());
queue.push_back(node.true_child.unwrap());
} else {
queue.push_back(node.false_child.unwrap());
}
} }
} }
None => break, None => break,
}; };
} }
return result; result
} }
fn find_best_cutoff<M: Matrix<T>>( fn find_best_cutoff<M: Matrix<T>>(
&mut self, &mut self,
visitor: &mut NodeVisitor<T, M>, visitor: &mut NodeVisitor<'_, T, M>,
mtry: usize, mtry: usize,
) -> bool { ) -> bool {
let (n_rows, n_attr) = visitor.x.shape(); let (n_rows, n_attr) = visitor.x.shape();
@@ -456,7 +456,7 @@ impl<T: RealNumber> DecisionTreeClassifier<T> {
fn find_best_split<M: Matrix<T>>( fn find_best_split<M: Matrix<T>>(
&mut self, &mut self,
visitor: &mut NodeVisitor<T, M>, visitor: &mut NodeVisitor<'_, T, M>,
n: usize, n: usize,
count: &Vec<usize>, count: &Vec<usize>,
false_count: &mut Vec<usize>, false_count: &mut Vec<usize>,
@@ -530,7 +530,7 @@ impl<T: RealNumber> DecisionTreeClassifier<T> {
for i in 0..n { for i in 0..n {
if visitor.samples[i] > 0 { if visitor.samples[i] > 0 {
if visitor.x.get(i, self.nodes[visitor.node].split_feature) if visitor.x.get(i, self.nodes[visitor.node].split_feature)
<= self.nodes[visitor.node].split_value.unwrap_or(T::nan()) <= self.nodes[visitor.node].split_value.unwrap_or_else(T::nan)
{ {
true_samples[i] = visitor.samples[i]; true_samples[i] = visitor.samples[i];
tc += true_samples[i]; tc += true_samples[i];
+25 -27
View File
@@ -113,8 +113,8 @@ impl Default for DecisionTreeRegressorParameters {
impl<T: RealNumber> Node<T> { impl<T: RealNumber> Node<T> {
fn new(index: usize, output: T) -> Self { fn new(index: usize, output: T) -> Self {
Node { Node {
index: index, index,
output: output, output,
split_feature: 0, split_feature: 0,
split_value: Option::None, split_value: Option::None,
split_score: Option::None, split_score: Option::None,
@@ -144,14 +144,14 @@ impl<T: RealNumber> PartialEq for Node<T> {
impl<T: RealNumber> PartialEq for DecisionTreeRegressor<T> { impl<T: RealNumber> PartialEq for DecisionTreeRegressor<T> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.depth != other.depth || self.nodes.len() != other.nodes.len() { if self.depth != other.depth || self.nodes.len() != other.nodes.len() {
return false; false
} else { } else {
for i in 0..self.nodes.len() { for i in 0..self.nodes.len() {
if self.nodes[i] != other.nodes[i] { if self.nodes[i] != other.nodes[i] {
return false; return false;
} }
} }
return true; true
} }
} }
} }
@@ -177,14 +177,14 @@ impl<'a, T: RealNumber, M: Matrix<T>> NodeVisitor<'a, T, M> {
level: u16, level: u16,
) -> Self { ) -> Self {
NodeVisitor { NodeVisitor {
x: x, x,
y: y, y,
node: node_id, node: node_id,
samples: samples, samples,
order: order, order,
true_child_output: T::zero(), true_child_output: T::zero(),
false_child_output: T::zero(), false_child_output: T::zero(),
level: level, level,
} }
} }
} }
@@ -221,7 +221,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
let mut sum = T::zero(); let mut sum = T::zero();
for i in 0..y_ncols { for i in 0..y_ncols {
n += samples[i]; n += samples[i];
sum = sum + T::from(samples[i]).unwrap() * y_m.get(0, i); sum += T::from(samples[i]).unwrap() * y_m.get(0, i);
} }
let root = Node::new(0, sum / T::from(n).unwrap()); let root = Node::new(0, sum / T::from(n).unwrap());
@@ -233,14 +233,14 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
} }
let mut tree = DecisionTreeRegressor { let mut tree = DecisionTreeRegressor {
nodes: nodes, nodes,
parameters: parameters, parameters,
depth: 0, depth: 0,
}; };
let mut visitor = NodeVisitor::<T, M>::new(0, samples, &order, &x, &y_m, 1); let mut visitor = NodeVisitor::<T, M>::new(0, samples, &order, &x, &y_m, 1);
let mut visitor_queue: LinkedList<NodeVisitor<T, M>> = LinkedList::new(); let mut visitor_queue: LinkedList<NodeVisitor<'_, T, M>> = LinkedList::new();
if tree.find_best_cutoff(&mut visitor, mtry) { if tree.find_best_cutoff(&mut visitor, mtry) {
visitor_queue.push_back(visitor); visitor_queue.push_back(visitor);
@@ -282,24 +282,24 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
let node = &self.nodes[node_id]; let node = &self.nodes[node_id];
if node.true_child == None && node.false_child == None { if node.true_child == None && node.false_child == None {
result = node.output; result = node.output;
} else if x.get(row, node.split_feature)
<= node.split_value.unwrap_or_else(T::nan)
{
queue.push_back(node.true_child.unwrap());
} else { } else {
if x.get(row, node.split_feature) <= node.split_value.unwrap_or(T::nan()) { queue.push_back(node.false_child.unwrap());
queue.push_back(node.true_child.unwrap());
} else {
queue.push_back(node.false_child.unwrap());
}
} }
} }
None => break, None => break,
}; };
} }
return result; result
} }
fn find_best_cutoff<M: Matrix<T>>( fn find_best_cutoff<M: Matrix<T>>(
&mut self, &mut self,
visitor: &mut NodeVisitor<T, M>, visitor: &mut NodeVisitor<'_, T, M>,
mtry: usize, mtry: usize,
) -> bool { ) -> bool {
let (_, n_attr) = visitor.x.shape(); let (_, n_attr) = visitor.x.shape();
@@ -333,7 +333,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
fn find_best_split<M: Matrix<T>>( fn find_best_split<M: Matrix<T>>(
&mut self, &mut self,
visitor: &mut NodeVisitor<T, M>, visitor: &mut NodeVisitor<'_, T, M>,
n: usize, n: usize,
sum: T, sum: T,
parent_gain: T, parent_gain: T,
@@ -348,8 +348,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
if prevx.is_nan() || visitor.x.get(*i, j) == prevx { if prevx.is_nan() || visitor.x.get(*i, j) == prevx {
prevx = visitor.x.get(*i, j); prevx = visitor.x.get(*i, j);
true_count += visitor.samples[*i]; true_count += visitor.samples[*i];
true_sum = true_sum += T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i);
true_sum + T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i);
continue; continue;
} }
@@ -360,8 +359,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
{ {
prevx = visitor.x.get(*i, j); prevx = visitor.x.get(*i, j);
true_count += visitor.samples[*i]; true_count += visitor.samples[*i];
true_sum = true_sum += T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i);
true_sum + T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i);
continue; continue;
} }
@@ -384,7 +382,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
} }
prevx = visitor.x.get(*i, j); prevx = visitor.x.get(*i, j);
true_sum = true_sum + T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i); true_sum += T::from(visitor.samples[*i]).unwrap() * visitor.y.get(0, *i);
true_count += visitor.samples[*i]; true_count += visitor.samples[*i];
} }
} }
@@ -404,7 +402,7 @@ impl<T: RealNumber> DecisionTreeRegressor<T> {
for i in 0..n { for i in 0..n {
if visitor.samples[i] > 0 { if visitor.samples[i] > 0 {
if visitor.x.get(i, self.nodes[visitor.node].split_feature) if visitor.x.get(i, self.nodes[visitor.node].split_feature)
<= self.nodes[visitor.node].split_value.unwrap_or(T::nan()) <= self.nodes[visitor.node].split_value.unwrap_or_else(T::nan)
{ {
true_samples[i] = visitor.samples[i]; true_samples[i] = visitor.samples[i];
tc += true_samples[i]; tc += true_samples[i];