# Peroxide
[![On crates.io](https://img.shields.io/crates/v/peroxide.svg)](https://crates.io/crates/peroxide)
[![On docs.rs](https://docs.rs/peroxide/badge.svg)](https://docs.rs/peroxide/)
![maintenance](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
Pure Rust numeric library contains linear algebra, numerical analysis, statistics and machine learning tools with R, MATLAB, Python like macros.
## Latest README version
Corresponding to `0.11.2`.
## Pre-requisite
* Python module : `matplotlib` for plotting
## Install
* Add next line to your `cargo.toml`
```toml
peroxide = "0.11"
```
## Module Structure
- __src__
- [lib.rs](src/lib.rs) : `mod` and `re-export`
- __macros__ : Macro files
- [matlab_macro.rs](src/macros/matlab_macro.rs) : MATLAB like macro
- [mod.rs](src/macros/mod.rs)
- [r_macro.rs](src/macros/r_macro.rs) : R like macro
- __ml__ : For machine learning (*Beta*)
- [mod.rs](src/ml/mod.rs)
- [reg.rs](src/ml/reg.rs) : Regression tools
- __numerical__ : To do numerical things
- [bdf.rs](src/numerical/bdf.rs) : Backward Differentiation Formula
- [gauss_legendre.rs](src/numerical/gauss_legendre.rs) : Gauss-Legendre 4th order
- [interp.rs](src/numerical/interp.rs) : Interpolation
- [mod.rs](src/numerical/mod.rs)
- [newton.rs](src/numerical/newton.rs) : Newton's Method
- [ode.rs](src/grave/ode.rs) : Merge all ODE algorithm to one module
- [spline.rs](src/numerical/spline.rs) : Natural Spline
- [utils.rs](src/numerical/utils.rs) : Utils to do numerical things (e.g. jacobian)
- __operation__ : To define general operations
- [extra_ops.rs](src/operation/extra_ops.rs) : Missing operations & Real Trait
- [mut_ops.rs](src/operation/mut_ops.rs) : Mutable operations
- [mod.rs](src/operation/mod.rs)
- __special__ : Wrapper for `special` crate
- [mod.rs](src/special/mod.rs)
- [function.rs](src/special/function.rs) : Special functions
- __statistics__ : Statistical Tools
- [mod.rs](src/statistics/mod.rs)
- [dist.rs](src/statistics/dist.rs) : Probability distributions
- [ops.rs](src/statistics/ops.rs) : Some probabilistic operations
- [rand.rs](src/statistics/rand.rs) : Wrapper for `rand` crate
- [stat.rs](src/statistics/stat.rs) : Statistical tools
- __structure__ : Fundamental data structures
- [dataframe.rs](src/structure/dataframe.rs) : Not yet implemented
- [dual.rs](src/structure/dual.rs) : Dual number system for automatic differentiation
- [hyper_dual.rs](src/structure/hyper_dual.rs) : Hyper dual number system for automatic differentiation
- [matrix.rs](src/structure/matrix.rs) : Matrix
- [multinomial.rs](src/structure/multinomial.rs) : For multinomial (*Beta*)
- [mod.rs](src/structure/mod.rs)
- [polynomial.rs](src/structure/polynomial.rs) : Polynomial
- [vector.rs](src/structure/vector.rs) : Extra tools for `Vec<f64>`
- __util__
- [mod.rs](src/util/mod.rs)
- [api.rs](src/util/api.rs) : Matrix constructor for various language style
- [non_macro.rs](src/util/non_macro.rs) : Primordial version of macros
- [pickle.rs](src/util/pickle.rs) : To handle `pickle` data structure
- [plot.rs](src/util/plot.rs) : To draw plot (using `pyo3`)
- [print.rs](src/util/print.rs) : To print conveniently
- [useful.rs](src/util/useful.rs) : Useful utils to implement library
- [writer.rs](src/util/writer.rs) : More convenient write system
## Documentation
* Modifying...
[![On docs.rs](https://docs.rs/peroxide/badge.svg)](https://docs.rs/peroxide/)
## Example
### Basic Runge-Kutta 4th order with inline-python
```rust
#![feature(proc_macro_hygiene)]
extern crate peroxide;
extern crate inline_python;
use peroxide::*;
use inline_python::python;
fn main() {
// Initial condition
let init_state = State::<f64>::new(0f64, c!(1), c!(0));
let mut ode_solver = ExplicitODE::new(test_fn);
ode_solver
.set_method(ExMethod::RK4)
.set_initial_condition(init_state)
.set_step_size(0.01)
.set_times(1000);
let result = ode_solver.integrate();
let x = result.col(0);
let y = result.col(1);
// Plot (Thanks to inline-python)
python! {
import pylab as plt
plt.plot('x, 'y)
plt.show()
}
}
// dy/dx = (5x^2 - y) / e^(x+y)
fn test_fn(st: &mut State<f64>) {
let x = st.param;
let y = &st.value;
let dy = &mut st.deriv;
dy[0] = (5f64 * x.powi(2) - y[0]) / (x + y[0]).exp();
}
```
### Basic Runge-Kutta 4th order with advanced plotting
```rust
extern crate peroxide;
use peroxide::*;
fn main() {
let init_state = State::<f64>::new(0f64, c!(1), c!(0));
let mut ode_solver = ExplicitODE::new(test_fn);
ode_solver
.set_method(ExMethod::RK4)
.set_initial_condition(init_state)
.set_step_size(0.01)
.set_times(1000);
let result = ode_solver.integrate();
let x = result.col(0);
let y = result.col(1);
// Plot (using python matplotlib)
let mut plt = Plot2D::new();
plt.set_domain(x)
.insert_image(y)
.set_title("Test Figure")
.set_fig_size((10, 6))
.set_dpi(300)
.set_legends(vec!["RK4".to_owned()])
.set_path("example_data/test_plot.png");
plt.savefig();
}
fn test_fn(st: &mut State<f64>) {
let x = st.param;
let y = &st.value;
let dy = &mut st.deriv;
dy[0] = (5f64 * x.powi(2) - y[0]) / (x + y[0]).exp();
}
```
### Basic Runge-Kutta 4th order with Stop condition
```rust
extern crate peroxide;
use peroxide::*;
fn main() {
let init_state = State::<f64>::new(0f64, c!(1), c!(0));
let mut ode_solver = ExplicitODE::new(test_fn);
ode_solver
.set_method(ExMethod::RK4)
.set_initial_condition(init_state)
.set_step_size(0.01)
.set_stop_condition(stop) // Add stop condition
.set_times(1000);
let result = ode_solver.integrate();
let x = result.col(0);
let y = result.col(1);
let mut plt = Plot2D::new();
plt.set_domain(x)
.insert_image(y)
.set_title("Test Figure")
.set_fig_size((10, 6))
.set_dpi(300)
.set_legends(vec!["RK4".to_owned()])
.set_path("example_data/test_plot.png");
plt.savefig();
}
fn test_fn(st: &mut State<f64>) {
let x = st.param;
let y = &st.value;
let dy = &mut st.deriv;
dy[0] = (5f64 * x.powi(2) - y[0]) / (x + y[0]).exp();
}
fn stop(st: &ExplicitODE) -> bool {
let y = &st.get_state().value[0];
(*y - 2.4).abs() < 0.01
}
```
![Example image](example_data/test_plot.png)
### Multi-Layer Perceptron (from scratch)
```rust
extern crate peroxide;
use peroxide::*;
// x : n x L
// xb: n x (L+1)
// v : (L+1) x M
// a : n x M
// ab: n x (M+1)
// w : (M+1) x n
// wb: M x N
// y : n x N
// t : n x N
// dh: n x M
// do: n x N
fn main() {
let v = weights_init(3, 2);
let w = weights_init(3, 1);
let x = ml_matrix("0 0; 0 1; 1 0; 1 1");
let t = ml_matrix("0;1;1;0");
let y = train(v, w, x, t, 0.25, 5000);
y.print();
}
fn weights_init(m: usize, n: usize) -> Matrix {
rand(m, n) * 2f64 - 1f64
}
fn sigmoid(x: f64) -> f64 {
1f64 / (1f64 + (-x).exp())
}
fn forward(weights: Matrix, input_bias: Matrix) -> Matrix {
let s = input_bias * weights;
s.fmap(|x| sigmoid(x))
}
fn add_bias(input: Matrix, bias: f64) -> Matrix {
let b = matrix(vec![bias; input.row], input.row, 1, Col);
cbind(b, input)
}
fn hide_bias(weight: Matrix) -> Matrix {
weight.skip(1, Row)
}
fn train(
weights1: Matrix,
weights2: Matrix,
input: Matrix,
answer: Matrix,
eta: f64,
times: usize,
) -> Matrix {
let x = input;
let mut v = weights1;
let mut w = weights2;
let t = answer;
let xb = add_bias(x.clone(), -1f64);
for _i in 0..times {
let a = forward(v.clone(), xb.clone());
let ab = add_bias(a.clone(), -1f64);
let y = forward(w.clone(), ab.clone());
// let err = (y.clone() - t.clone()).t() * (y.clone() - t.clone());
let wb = hide_bias(w.clone());
let delta_o = (y.clone() - t.clone()) * y.clone() * (1f64 - y.clone());
let delta_h = (delta_o.clone() * wb.t()) * a.clone() * (1f64 - a.clone());
w = w.clone() - eta * (ab.t() * delta_o);
v = v.clone() - eta * (xb.t() * delta_h);
}
let a = forward(v, xb);
let ab = add_bias(a, -1f64);
let y = forward(w, ab);
y
}
```
## Version Info
To see [RELEASES.md](./RELEASES.md)
## TODO
To see [TODO.md](./TODO.md)