Skip to content
This repository has been archived by the owner on Jul 16, 2021. It is now read-only.

Commit

Permalink
Bump to rulinalg 0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
sinhrks committed Dec 29, 2016
1 parent 5265129 commit 4a06bb8
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 68 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ datasets = []
[dependencies]
num = { version = "0.1.35", default-features = false }
rand = "0.3.14"
rulinalg = "0.3.7"
rulinalg = "0.4.0"
4 changes: 2 additions & 2 deletions examples/k-means_generating_cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ fn generate_data(centroids: &Matrix<f64>,

for _ in 0..points_per_centroid {
// Generate points from each centroid
for centroid in centroids.iter_rows() {
for centroid in centroids.row_iter() {
// Generate a point randomly around the centroid
let mut point = Vec::with_capacity(centroids.cols());
for feature in centroid {
for feature in centroid.iter() {
point.push(feature + normal_rv.ind_sample(&mut rng));
}

Expand Down
12 changes: 6 additions & 6 deletions examples/naive_bayes_dogs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,16 @@ fn main() {
// Score how well we did.
let mut hits = 0;
let unprinted_total = test_set_size.saturating_sub(10) as usize;
for (dog, prediction) in test_dogs.iter().zip(predictions.iter_rows()).take(unprinted_total) {
evaluate_prediction(&mut hits, dog, prediction);
for (dog, prediction) in test_dogs.iter().zip(predictions.row_iter()).take(unprinted_total) {
evaluate_prediction(&mut hits, dog, prediction.raw_slice());
}

if unprinted_total > 0 {
println!("...");
}
for (dog, prediction) in test_dogs.iter().zip(predictions.iter_rows()).skip(unprinted_total) {
let (actual_color, accurate) = evaluate_prediction(&mut hits, dog, prediction);

for (dog, prediction) in test_dogs.iter().zip(predictions.row_iter()).skip(unprinted_total) {
let (actual_color, accurate) = evaluate_prediction(&mut hits, dog, prediction.raw_slice());
println!("Predicted: {:?}; Actual: {:?}; Accurate? {:?}",
dog.color, actual_color, accurate);
}
Expand Down
49 changes: 46 additions & 3 deletions src/analysis/score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,24 @@ pub fn accuracy<I>(outputs: I, targets: I) -> f64
correct as f64 / len
}


/// Returns the fraction of outputs rows which match their target.
pub fn row_accuracy(outputs: &Matrix<f64>, targets: &Matrix<f64>) -> f64 {
accuracy(outputs.iter_rows(), targets.iter_rows())
pub fn row_accuracy<T: PartialEq>(outputs: &Matrix<T>, targets: &Matrix<T>) -> f64 {

assert!(outputs.rows() == targets.rows());
let len = outputs.rows() as f64;

let correct = outputs.row_iter()
.zip(targets.row_iter())
.filter(|&(ref x, ref y)| x.raw_slice()
.iter()
.zip(y.raw_slice())
.all(|(v1, v2)| v1 == v2))
.count();
correct as f64 / len

// Row doesn't impl PartialEq
// accuracy(outputs.row_iter(), targets.row_iter())
}

// TODO: generalise to accept arbitrary iterators of diff-able things
Expand All @@ -37,7 +52,7 @@ pub fn neg_mean_squared_error(outputs: &Matrix<f64>, targets: &Matrix<f64>) -> f
#[cfg(test)]
mod tests {
use linalg::Matrix;
use super::{accuracy, neg_mean_squared_error};
use super::{accuracy, row_accuracy, neg_mean_squared_error};

#[test]
fn test_accuracy() {
Expand All @@ -46,6 +61,34 @@ mod tests {
assert_eq!(accuracy(outputs.iter(), targets.iter()), 2f64/3f64);
}

#[test]
fn test_row_accuracy() {
let outputs = matrix![1, 0;
0, 1;
1, 0];
let targets = matrix![1, 0;
0, 1;
1, 0];
assert_eq!(row_accuracy(&outputs, &targets), 1.0);

let outputs = matrix![1, 0;
0, 1;
1, 0];
let targets = matrix![0, 1;
0, 1;
1, 0];
assert_eq!(row_accuracy(&outputs, &targets), 2. / 3.);

let outputs = matrix![1., 0.;
0., 1.;
1., 0.];
let targets = matrix![0., 1.;
0., 1.;
1., 0.];
assert_eq!(row_accuracy(&outputs, &targets), 2. / 3.);
}


#[test]
fn test_neg_mean_squared_error_1d() {
let outputs = Matrix::new(3, 1, vec![1f64, 2f64, 3f64]);
Expand Down
12 changes: 6 additions & 6 deletions src/data/transforms/minmax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl<T: Float> Transformer<Matrix<T>> for MinMaxScaler<T> {

let mut input_min_max = vec![(T::max_value(), T::min_value()); features];

for row in inputs.iter_rows() {
for row in inputs.row_iter() {
for (idx, (feature, min_max)) in row.into_iter().zip(input_min_max.iter_mut()).enumerate() {
if !feature.is_finite() {
return Err(Error::new(ErrorKind::InvalidData,
Expand All @@ -106,7 +106,7 @@ impl<T: Float> Transformer<Matrix<T>> for MinMaxScaler<T> {
min_max.1 = *feature;
}


}
}

Expand All @@ -130,12 +130,12 @@ impl<T: Float> Transformer<Matrix<T>> for MinMaxScaler<T> {
.map(|(&(_, x), &s)| self.scaled_max - x * s)
.collect::<Vec<_>>();

for row in inputs.iter_rows_mut() {
utils::in_place_vec_bin_op(row, &scales, |x, &y| {
for mut row in inputs.row_iter_mut() {
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &scales, |x, &y| {
*x = *x * y;
});

utils::in_place_vec_bin_op(row, &consts, |x, &y| {
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &consts, |x, &y| {
*x = *x + y;
});
}
Expand All @@ -157,7 +157,7 @@ impl<T: Float> Invertible<Matrix<T>> for MinMaxScaler<T> {
"Inputs have different feature count than transformer."));
}

for row in inputs.iter_rows_mut() {
for mut row in inputs.row_iter_mut() {
for i in 0..features {
row[i] = (row[i] - consts[i]) / scales[i];
}
Expand Down
12 changes: 6 additions & 6 deletions src/data/transforms/standardize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ impl<T: Float + FromPrimitive> Transformer<Matrix<T>> for Standardizer<T> {
return Err(Error::new(ErrorKind::InvalidData, "Some data point is non-finite."));
}

for row in inputs.iter_rows_mut() {
for mut row in inputs.row_iter_mut() {
// Subtract the mean
utils::in_place_vec_bin_op(row, &mean.data(), |x, &y| *x = *x - y);
utils::in_place_vec_bin_op(row, &variance.data(), |x, &y| {
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &mean.data(), |x, &y| *x = *x - y);
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &variance.data(), |x, &y| {
*x = (*x * self.scaled_stdev / y.sqrt()) + self.scaled_mean
});
}
Expand All @@ -126,13 +126,13 @@ impl<T: Float + FromPrimitive> Invertible<Matrix<T>> for Standardizer<T> {
"Inputs have different feature count than transformer."));
}

for row in inputs.iter_rows_mut() {
utils::in_place_vec_bin_op(row, &variances.data(), |x, &y| {
for mut row in inputs.row_iter_mut() {
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &variances.data(), |x, &y| {
*x = (*x - self.scaled_mean) * y.sqrt() / self.scaled_stdev
});

// Add the mean
utils::in_place_vec_bin_op(row, &means.data(), |x, &y| *x = *x + y);
utils::in_place_vec_bin_op(&mut row.raw_slice_mut(), &means.data(), |x, &y| *x = *x + y);
}

Ok(inputs)
Expand Down
20 changes: 11 additions & 9 deletions src/learning/dbscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ impl UnSupModel<Matrix<f64>, Vector<Option<usize>>> for DBSCAN {
self.init_params(inputs.rows());
let mut cluster = 0;

for (idx, point) in inputs.iter_rows().enumerate() {
for (idx, point) in inputs.row_iter().enumerate() {
let visited = self._visited[idx];

if !visited {
self._visited[idx] = true;

let neighbours = self.region_query(point, inputs);
let neighbours = self.region_query(point.raw_slice(), inputs);

if neighbours.len() >= self.min_points {
self.expand_cluster(inputs, idx, neighbours, cluster);
Expand All @@ -108,12 +108,14 @@ impl UnSupModel<Matrix<f64>, Vector<Option<usize>>> for DBSCAN {
&self.clusters) {
let mut classes = Vec::with_capacity(inputs.rows());

for input_point in inputs.iter_rows() {
for input_point in inputs.row_iter() {
let mut distances = Vec::with_capacity(cluster_data.rows());

for cluster_point in cluster_data.iter_rows() {
for cluster_point in cluster_data.row_iter() {
let point_distance =
utils::vec_bin_op(input_point, cluster_point, |x, y| x - y);
utils::vec_bin_op(input_point.raw_slice(),
cluster_point.raw_slice(),
|x, y| x - y);
distances.push(utils::dot(&point_distance, &point_distance).sqrt());
}

Expand Down Expand Up @@ -182,8 +184,8 @@ impl DBSCAN {
let visited = self._visited[*data_point_idx];
if !visited {
self._visited[*data_point_idx] = true;
let data_point_row = unsafe { inputs.get_row_unchecked(*data_point_idx) };
let sub_neighbours = self.region_query(data_point_row, inputs);
let data_point_row = unsafe { inputs.row_unchecked(*data_point_idx) };
let sub_neighbours = self.region_query(data_point_row.raw_slice(), inputs);

if sub_neighbours.len() >= self.min_points {
self.expand_cluster(inputs, *data_point_idx, sub_neighbours, cluster);
Expand All @@ -198,8 +200,8 @@ impl DBSCAN {
"point must be of same dimension as inputs");

let mut in_neighbourhood = Vec::new();
for (idx, data_point) in inputs.iter_rows().enumerate() {
let point_distance = utils::vec_bin_op(data_point, point, |x, y| x - y);
for (idx, data_point) in inputs.row_iter().enumerate() {
let point_distance = utils::vec_bin_op(data_point.raw_slice(), point, |x, y| x - y);
let dist = utils::dot(&point_distance, &point_distance).sqrt();

if dist < self.eps {
Expand Down
14 changes: 7 additions & 7 deletions src/learning/gmm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl GaussianMixtureModel {
if mixture_weights.size() != k {
Err(Error::new(ErrorKind::InvalidParameters, "Mixture weights must have length k."))
} else if mixture_weights.data().iter().any(|&x| x < 0f64) {
Err(Error::new(ErrorKind::InvalidParameters, "Mixture weights must have only non-negative entries."))
Err(Error::new(ErrorKind::InvalidParameters, "Mixture weights must have only non-negative entries."))
} else {
let sum = mixture_weights.sum();
let normalized_weights = mixture_weights / sum;
Expand Down Expand Up @@ -233,9 +233,9 @@ impl GaussianMixtureModel {
CovOption::Full | CovOption::Regularized(_) => {
let means = inputs.mean(Axes::Row);
let mut cov_mat = Matrix::zeros(inputs.cols(), inputs.cols());
for (j, row) in cov_mat.iter_rows_mut().enumerate() {
for (j, mut row) in cov_mat.row_iter_mut().enumerate() {
for (k, elem) in row.iter_mut().enumerate() {
*elem = inputs.iter_rows().map(|r| {
*elem = inputs.row_iter().map(|r| {
(r[j] - means[j]) * (r[k] - means[k])
}).sum::<f64>();
}
Expand All @@ -259,10 +259,10 @@ impl GaussianMixtureModel {
let mut cov_invs = Vec::with_capacity(self.comp_count);

if let Some(ref covars) = self.model_covars {
for cov in covars {
for cov in covars.iter() {
// TODO: combine these. We compute det to get the inverse.
let covar_det = cov.det();
let covar_inv = try!(cov.inverse().map_err(Error::from));
let covar_det = cov.clone().det();
let covar_inv = try!(cov.clone().inverse().map_err(Error::from));

cov_sqrt_dets.push(covar_det.sqrt());
cov_invs.push(covar_inv);
Expand Down Expand Up @@ -309,7 +309,7 @@ impl GaussianMixtureModel {

let mut new_means = membership_weights.transpose() * inputs;

for (mean, w) in new_means.iter_rows_mut().zip(sum_weights.data().iter()) {
for (mut mean, w) in new_means.row_iter_mut().zip(sum_weights.data().iter()) {
for m in mean.iter_mut() {
*m /= *w;
}
Expand Down
10 changes: 5 additions & 5 deletions src/learning/gp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ impl<T: Kernel, U: MeanFunc> GaussianProcess<T, U> {
let dim2 = m2.rows();

let mut ker_data = Vec::with_capacity(dim1 * dim2);
ker_data.extend(m1.iter_rows().flat_map(|row1| {
m2.iter_rows()
.map(move |row2| self.ker.kernel(row1, row2))
ker_data.extend(m1.row_iter().flat_map(|row1| {
m2.row_iter()
.map(move |row2| self.ker.kernel(row1.raw_slice(), row2.raw_slice()))
}));

Ok(Matrix::new(dim1, dim2, ker_data))
Expand Down Expand Up @@ -195,8 +195,8 @@ impl<T: Kernel, U: MeanFunc> GaussianProcess<T, U> {

let test_mat = try!(self.ker_mat(inputs, t_data));
let mut var_data = Vec::with_capacity(inputs.rows() * inputs.cols());
for row in test_mat.iter_rows() {
let test_point = Vector::new(row.to_vec());
for row in test_mat.row_iter() {
let test_point = Vector::new(row.raw_slice());
var_data.append(&mut t_mat.solve_l_triangular(test_point).unwrap().into_vec());
}

Expand Down
4 changes: 2 additions & 2 deletions src/learning/k_means.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl Initializer for KPlusPlus {
let first_cen = rng.gen_range(0usize, inputs.rows());

unsafe {
init_centroids.extend_from_slice(inputs.get_row_unchecked(first_cen));
init_centroids.extend_from_slice(inputs.row_unchecked(first_cen).raw_slice());
}

for i in 1..k {
Expand All @@ -350,7 +350,7 @@ impl Initializer for KPlusPlus {
}

let next_cen = sample_discretely(dist);
init_centroids.extend_from_slice(inputs.get_row_unchecked(next_cen));
init_centroids.extend_from_slice(inputs.row_unchecked(next_cen).raw_slice());
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/learning/naive_bayes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ impl<T: Distribution> NaiveBayes<T> {
self.class_counts = vec![0; class_count];
let mut class_data = vec![Vec::new(); class_count];

for (idx, row) in targets.iter_rows().enumerate() {
for (idx, row) in targets.row_iter().enumerate() {
// Find the class of this input
let class = try!(NaiveBayes::<T>::find_class(row));
let class = try!(NaiveBayes::<T>::find_class(row.raw_slice()));

// Note the class of the input
class_data[class].push(idx);
Expand Down Expand Up @@ -199,9 +199,9 @@ impl<T: Distribution> NaiveBayes<T> {
fn get_classes(log_probs: Matrix<f64>) -> Vec<usize> {
let mut data_classes = Vec::with_capacity(log_probs.rows());

data_classes.extend(log_probs.iter_rows().map(|row| {
data_classes.extend(log_probs.row_iter().map(|row| {
// Argmax each class log-probability per input
let (class, _) = utils::argmax(row);
let (class, _) = utils::argmax(row.raw_slice());
class
}));

Expand Down
2 changes: 1 addition & 1 deletion src/learning/nnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ impl<'a, T: Criterion> BaseNeuralNet<'a, T> {
/// Gets the weights for a layer excluding the bias weights.
fn get_non_bias_weights(&self, weights: &[f64], idx: usize) -> MatrixSlice<f64> {
let layer_weights = self.get_layer_weights(weights, idx);
layer_weights.reslice([1, 0], layer_weights.rows() - 1, layer_weights.cols())
layer_weights.sub_slice([1, 0], layer_weights.rows() - 1, layer_weights.cols())
}

/// Compute the gradient using the back propagation algorithm.
Expand Down
10 changes: 5 additions & 5 deletions src/learning/svm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ impl<K: Kernel> SVM<K> {
let dim2 = m2.rows();

let mut ker_data = Vec::with_capacity(dim1 * dim2);
ker_data.extend(m1.iter_rows().flat_map(|row1| {
m2.iter_rows()
.map(move |row2| self.ker.kernel(row1, row2))
ker_data.extend(m1.row_iter().flat_map(|row1| {
m2.row_iter()
.map(move |row2| self.ker.kernel(row1.raw_slice(), row2.raw_slice()))
}));

Ok(Matrix::new(dim1, dim2, ker_data))
Expand Down Expand Up @@ -154,8 +154,8 @@ impl<K: Kernel> SupModel<Matrix<f64>, Vector<f64>> for SVM<K> {
for t in 0..self.optim_iters {
let i = rng.gen_range(0, n);
let row_i = full_inputs.select_rows(&[i]);
let sum = full_inputs.iter_rows()
.fold(0f64, |sum, row| sum + self.ker.kernel(row_i.data(), row)) *
let sum = full_inputs.row_iter()
.fold(0f64, |sum, row| sum + self.ker.kernel(row_i.data(), row.raw_slice())) *
targets[i] / (self.lambda * (t as f64));

if sum < 1f64 {
Expand Down
Loading

0 comments on commit 4a06bb8

Please sign in to comment.