Initial commit
This commit is contained in:
@@ -8,3 +8,11 @@ Cargo.lock
|
|||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.project
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "smartcore"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Vlad Orlov"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ndarray = "0.12.1"
|
||||||
|
ndarray-linalg = "0.10"
|
||||||
|
num-traits = "0.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
ndarray = "0.12.1"
|
||||||
|
criterion = "0.2"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "distance"
|
||||||
|
harness = false
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate criterion;
|
||||||
|
extern crate smartcore;
|
||||||
|
extern crate ndarray;
|
||||||
|
use ndarray::Array;
|
||||||
|
use smartcore::math::distance::euclidian::EuclidianDistance;
|
||||||
|
use smartcore::math::distance::Distance;
|
||||||
|
|
||||||
|
use criterion::Criterion;
|
||||||
|
use criterion::black_box;
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let a = Array::from_vec(vec![1., 2., 3.]);
|
||||||
|
|
||||||
|
c.bench_function("Euclidean Distance", move |b| b.iter(|| EuclidianDistance::distance(black_box(&a), black_box(&a))));
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod sort;
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
pub struct HeapSelect<T: std::cmp::Ord> {
|
||||||
|
|
||||||
|
k: usize,
|
||||||
|
n: usize,
|
||||||
|
sorted: bool,
|
||||||
|
heap: Vec<T>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::cmp::Ord> HeapSelect<T> {
|
||||||
|
|
||||||
|
pub fn from_vec(vec: Vec<T>) -> HeapSelect<T> {
|
||||||
|
HeapSelect{
|
||||||
|
k: vec.len(),
|
||||||
|
n: 0,
|
||||||
|
sorted: false,
|
||||||
|
heap: vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, element: T) {
|
||||||
|
self.sorted = false;
|
||||||
|
if self.n < self.k {
|
||||||
|
self.heap[self.n] = element;
|
||||||
|
self.n += 1;
|
||||||
|
if self.n == self.k {
|
||||||
|
self.heapify();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.n += 1;
|
||||||
|
if element.cmp(&self.heap[0]) == Ordering::Less {
|
||||||
|
self.heap[0] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn heapify(&mut self){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_vec() {
|
||||||
|
let heap = HeapSelect::from_vec(vec!(1, 2, 3));
|
||||||
|
assert_eq!(3, heap.k);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add() {
|
||||||
|
let mut heap = HeapSelect::from_vec(Vec::<i32>::new());
|
||||||
|
heap.add(1);
|
||||||
|
heap.add(2);
|
||||||
|
heap.add(3);
|
||||||
|
assert_eq!(3, heap.n);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod heap_select;
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
use super::Classifier;
|
||||||
|
use super::super::math::distance::Distance;
|
||||||
|
use super::super::math::distance::euclidian::EuclidianDistance;
|
||||||
|
use ndarray::prelude::*;
|
||||||
|
use num_traits::Signed;
|
||||||
|
use num_traits::Float;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub struct KNNClassifier<E> {
|
||||||
|
y: Option<Array1<E>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KNNAlgorithm<T>{
|
||||||
|
fn find(&self, from: &T, k: i32) -> &Vec<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SimpleKNNAlgorithm<T, A, D>
|
||||||
|
where
|
||||||
|
A: Float,
|
||||||
|
D: Distance<T, A>
|
||||||
|
{
|
||||||
|
data: Vec<T>,
|
||||||
|
distance: D,
|
||||||
|
__phantom: PhantomData<A>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, A, D> KNNAlgorithm<T> for SimpleKNNAlgorithm<T, A, D>
|
||||||
|
where
|
||||||
|
A: Float,
|
||||||
|
D: Distance<T, A>
|
||||||
|
{
|
||||||
|
fn find(&self, from: &T, k: i32) -> &Vec<T> {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A1, A2> Classifier<A1, A2> for KNNClassifier<A2>
|
||||||
|
where
|
||||||
|
A2: Signed + Clone,
|
||||||
|
{
|
||||||
|
fn fit(&mut self, x: &Array2<A1>, y: &Array1<A2>){
|
||||||
|
self.y = Some(Array1::<A2>::zeros(ArrayBase::len(y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn predict(&self, x: &Array2<A1>) -> Array1<A2>{
|
||||||
|
let array = Array1::<A2>::zeros(ArrayBase::len(self.y.as_ref().unwrap()));
|
||||||
|
array
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn knn_fit_predict() {
|
||||||
|
let mut knn = KNNClassifier{y: None};
|
||||||
|
let x = arr2(&[[1, 2, 3],[4, 5, 6]]);
|
||||||
|
let y = arr1(&[1, 2]);
|
||||||
|
knn.fit(&x, &y);
|
||||||
|
let r = knn.predict(&x);
|
||||||
|
assert_eq!(2, ArrayBase::len(&r));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn knn_find() {
|
||||||
|
let sKnn = SimpleKNNAlgorithm{
|
||||||
|
data: vec!(arr1(&[1., 2.]), arr1(&[1., 2.]), arr1(&[1., 2.])),
|
||||||
|
distance: EuclidianDistance{},
|
||||||
|
__phantom: PhantomData
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(&vec!(arr1(&[1., 2.]), arr1(&[1., 2.]), arr1(&[1., 2.])), sKnn.find(&arr1(&[1., 2.]), 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
use ndarray::prelude::*;
|
||||||
|
use ndarray::{arr1, arr2};
|
||||||
|
use ndarray::FixedInitializer;
|
||||||
|
|
||||||
|
pub mod knn;
|
||||||
|
|
||||||
|
pub trait Classifier<E1, E2>
|
||||||
|
{
|
||||||
|
|
||||||
|
fn fit(&mut self, x: &Array2<E1>, y: &Array1<E2>);
|
||||||
|
|
||||||
|
fn predict(&self, x: &Array2<E1>) -> Array1<E2>;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IllegalArgumentError {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IllegalArgumentError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod classification;
|
||||||
|
pub mod math;
|
||||||
|
pub mod error;
|
||||||
|
pub mod algorithm;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use super::Distance;
|
||||||
|
use ndarray::{ArrayBase, Data, Dimension};
|
||||||
|
use num_traits::Float;
|
||||||
|
|
||||||
|
pub struct EuclidianDistance{}
|
||||||
|
|
||||||
|
impl<A, S, D> Distance<ArrayBase<S, D>, A> for EuclidianDistance
|
||||||
|
where
|
||||||
|
A: Float,
|
||||||
|
S: Data<Elem = A>,
|
||||||
|
D: Dimension
|
||||||
|
{
|
||||||
|
|
||||||
|
fn distance(a: &ArrayBase<S, D>, b: &ArrayBase<S, D>) -> A {
|
||||||
|
if a.len() != b.len() {
|
||||||
|
panic!("vectors a and b have different length");
|
||||||
|
} else {
|
||||||
|
((a - b)*(a - b)).sum().sqrt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ndarray::{arr1, Array};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measure_simple_euclidian_distance() {
|
||||||
|
let a = Array::from_vec(vec![1., 2., 3.]);
|
||||||
|
let b = Array::from_vec(vec![4., 5., 6.]);
|
||||||
|
|
||||||
|
let d = EuclidianDistance::distance(&a, &b);
|
||||||
|
|
||||||
|
assert!((d - 5.19615242).abs() < 1e-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn measure_simple_euclidian_distance_static() {
|
||||||
|
let a = arr1(&[-2.1968219, -0.9559913, -0.0431738, 1.0567679, 0.3853515]);
|
||||||
|
let b = arr1(&[-1.7781325, -0.6659839, 0.9526148, -0.9460919, -0.3925300]);
|
||||||
|
|
||||||
|
let d = EuclidianDistance::distance(&a, &b);
|
||||||
|
|
||||||
|
assert!((d - 2.422302).abs() < 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
pub mod euclidian;
|
||||||
|
|
||||||
|
use num_traits::Float;
|
||||||
|
|
||||||
|
pub trait Distance<T, A>
|
||||||
|
where
|
||||||
|
A: Float
|
||||||
|
{
|
||||||
|
fn distance(a: &T, b: &T) -> A;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod distance;
|
||||||
Reference in New Issue
Block a user