peroxide/util/
non_macro.rs

1//! Macro to non macro function
2//!
3//! # R like non-macro functions
4//!
5//! - seq
6//! - seq_with_precision
7//! - rbind
8//! - cbind
9//!
10//! # MATLAB like non-macro functions
11//!
12//! - eye
13//! - eye_shape
14//! - zeros
15//! - zeros_shape
16//! - linspace
17//! - linspace_with_precision
18//! - rand
19//! - rand_with_rng
20//! - rand_with_dist
21//!
22//! # Numpy like non-macro functions
23//!
24//! - logspace
25//! - column_stack
26//! - row_stack
27//!
28//! # Haskell like non-macro functions
29//!
30//! - concat
31//! - cat
32
33extern crate rand;
34use self::rand::prelude::*;
35use crate::structure::{
36    matrix::Shape::{Col, Row},
37    matrix::{matrix, Matrix, Shape},
38};
39use crate::traits::float::FloatWithPrecision;
40use crate::traits::matrix::MatrixTrait;
41use anyhow::{bail, Result};
42use rand_distr::{Distribution, Uniform};
43
44#[derive(Debug, Copy, Clone)]
45pub enum ConcatenateError {
46    DifferentLength,
47}
48
49impl std::fmt::Display for ConcatenateError {
50    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51        match *self {
52            ConcatenateError::DifferentLength => write!(
53                f,
54                "To concatenate, vectors or matrices must have the same length"
55            ),
56        }
57    }
58}
59
60// ┌─────────────────────────────────────────────────────────┐
61//  R-like non-macro functions
62// └─────────────────────────────────────────────────────────┘
63/// R like seq function
64///
65/// # Example
66/// ```
67/// use peroxide::fuga::*;
68///
69/// let a = seq(1, 10, 2);
70/// assert_eq!(a, vec![1f64,3f64,5f64,7f64,9f64]);
71///
72/// let b = seq(1, 1, 1);
73/// assert_eq!(b, vec![1f64]);
74/// ```
75pub fn seq<S, T, U>(start: S, end: T, step: U) -> Vec<f64>
76where
77    S: Into<f64> + Copy,
78    T: Into<f64> + Copy,
79    U: Into<f64> + Copy,
80{
81    let s = start.into();
82    let e = end.into();
83    let step = step.into();
84
85    assert!(e >= s);
86
87    let factor: f64 = (e - s) / step;
88    let l: usize = factor.floor() as usize + 1;
89    let mut v: Vec<f64> = vec![0f64; l];
90
91    for (i, v) in v.iter_mut().enumerate() {
92        *v = s + step * (i as f64);
93    }
94    v
95}
96
97/// Seq with Precision
98///
99/// # Example
100/// ```
101/// use peroxide::fuga::*;
102///
103/// let x = seq(0, 1e-2, 1e-3);
104/// assert_ne!(x[9], 0.009);
105///
106/// let x = seq_with_precision(0, 1e-2, 1e-3, 3);
107/// assert_eq!(x[9], 0.009);
108/// ```
109pub fn seq_with_precision<S, T, U>(start: S, end: T, step: U, precision: usize) -> Vec<f64>
110where
111    S: Into<f64> + Copy,
112    T: Into<f64> + Copy,
113    U: Into<f64> + Copy,
114{
115    let s = start.into();
116    let e = end.into();
117    let step = step.into();
118
119    assert!(e >= s);
120
121    let factor: f64 = (e - s) / step;
122    let l: usize = factor.floor() as usize + 1;
123    let mut v: Vec<f64> = vec![0f64; l];
124
125    for (i, v) in v.iter_mut().enumerate() {
126        *v = (s + step * (i as f64)).round_with_precision(precision);
127    }
128    v
129}
130
131/// R like cbind - concatenate two matrix by column direction
132///
133/// # Examples
134/// ```
135/// #[macro_use]
136/// extern crate peroxide;
137/// use peroxide::fuga::*;
138///
139/// fn main() -> Result<(), Box<dyn std::error::Error>> {
140///     let a = matrix!(1;4;1, 2, 2, Col);
141///     let b = matrix!(5;8;1, 2, 2, Col);
142///     let c = matrix!(1;8;1, 2, 4, Col);
143///     assert_eq!(cbind(a,b)?, c);
144///     Ok(())
145/// }
146/// ```
147pub fn cbind(m1: Matrix, m2: Matrix) -> Result<Matrix> {
148    let mut temp = m1;
149    if temp.shape != Col {
150        temp = temp.change_shape();
151    }
152
153    let mut temp2 = m2;
154    if temp2.shape != Col {
155        temp2 = temp2.change_shape();
156    }
157
158    let mut v = temp.data;
159    let mut c = temp.col;
160    let r = temp.row;
161
162    if r != temp2.row {
163        bail!(ConcatenateError::DifferentLength);
164    }
165    v.extend_from_slice(&temp2.data[..]);
166    c += temp2.col;
167    Ok(matrix(v, r, c, Col))
168}
169
170/// R like rbind - concatenate two matrix by row direction
171///
172/// # Examples
173/// ```
174/// #[macro_use]
175/// extern crate peroxide;
176/// use peroxide::fuga::*;
177///
178/// fn main() -> Result<(), Box<dyn std::error::Error>> {
179///     let a = matrix!(1;4;1, 2, 2, Row);
180///     let b = matrix!(5;8;1, 2, 2, Row);
181///     let c = matrix!(1;8;1, 4, 2, Row);
182///     assert_eq!(rbind(a,b)?, c);
183///     Ok(())
184/// }
185/// ```
186pub fn rbind(m1: Matrix, m2: Matrix) -> Result<Matrix> {
187    let mut temp = m1;
188    if temp.shape != Row {
189        temp = temp.change_shape();
190    }
191
192    let mut temp2 = m2;
193    if temp2.shape != Row {
194        temp2 = temp2.change_shape();
195    }
196
197    let mut v = temp.data;
198    let c = temp.col;
199    let mut r = temp.row;
200
201    if c != temp2.col {
202        bail!(ConcatenateError::DifferentLength);
203    }
204    v.extend_from_slice(&temp2.data[..]);
205    r += temp2.row;
206    Ok(matrix(v, r, c, Row))
207}
208
209// ┌─────────────────────────────────────────────────────────┐
210//  MATLAB like non-macro functions
211// └─────────────────────────────────────────────────────────┘
212/// MATLAB like zeros (Matrix)
213///
214/// # Examples
215/// ```
216/// use peroxide::fuga::*;
217///
218/// let a = zeros(2, 2);
219/// assert_eq!(a, matrix(vec![0f64;4], 2, 2, Row));
220/// ```
221pub fn zeros(r: usize, c: usize) -> Matrix {
222    matrix(vec![0f64; r * c], r, c, Row)
223}
224
225/// Zeros with custom shape
226pub fn zeros_shape(r: usize, c: usize, shape: Shape) -> Matrix {
227    matrix(vec![0f64; r * c], r, c, shape)
228}
229
230/// MATLAB like eye - Identity matrix
231///
232/// # Examples
233/// ```
234/// use peroxide::fuga::*;
235///
236/// let a = eye(2);
237/// assert_eq!(a, MATLAB::new("1 0;0 1"));
238/// ```
239pub fn eye(n: usize) -> Matrix {
240    let mut m = zeros(n, n);
241    for i in 0..n {
242        m[(i, i)] = 1f64;
243    }
244    m
245}
246
247/// eye with custom shape
248pub fn eye_shape(n: usize, shape: Shape) -> Matrix {
249    let mut m = zeros_shape(n, n, shape);
250    for i in 0..n {
251        m[(i, i)] = 1f64;
252    }
253    m
254}
255
256/// MATLAB like linspace
257///
258/// # Examples
259/// ```
260/// use peroxide::fuga::*;
261///
262/// let a = linspace(1, 10, 10);
263/// assert_eq!(a, seq(1,10,1));
264/// assert_eq!(a.len(), 10);
265/// ```
266pub fn linspace<S, T>(start: S, end: T, length: usize) -> Vec<f64>
267where
268    S: Into<f64> + Copy,
269    T: Into<f64> + Copy,
270{
271    let step: f64 = if length > 1 {
272        (end.into() - start.into()) / (length as f64 - 1f64)
273    } else {
274        0f64
275    };
276
277    let mut v = vec![0f64; length];
278    v[0] = start.into();
279    v[length - 1] = end.into();
280
281    for i in 1..length - 1 {
282        v[i] = v[0] + step * (i as f64);
283    }
284    v
285}
286
287/// linspace with precision
288///
289/// # Example
290/// ```
291/// use peroxide::fuga::*;
292///
293/// let x = linspace(0, 1e-2, 11);
294/// assert_ne!(x[9], 0.009);
295///
296/// let x = linspace_with_precision(0, 1e-2, 11, 3);
297/// assert_eq!(x[9], 0.009);
298/// ```
299pub fn linspace_with_precision<S, T>(start: S, end: T, length: usize, precision: usize) -> Vec<f64>
300where
301    S: Into<f64> + Copy,
302    T: Into<f64> + Copy,
303{
304    let step: f64 = if length > 1 {
305        (end.into() - start.into()) / (length as f64 - 1f64)
306    } else {
307        0f64
308    };
309
310    let mut v = vec![0f64; length];
311    v[0] = start.into().round_with_precision(precision);
312    v[length - 1] = end.into().round_with_precision(precision);
313
314    for i in 1..length - 1 {
315        v[i] = (v[0] + step * (i as f64)).round_with_precision(precision);
316    }
317    v
318}
319
320/// Rand matrix
321///
322/// # Description
323///
324/// Range = from 0 to 1
325pub fn rand(r: usize, c: usize) -> Matrix {
326    let mut rng = rand::rng();
327    rand_with_rng(r, c, &mut rng)
328}
329
330/// Rand matrix with specific rng
331///
332/// # Description
333///
334/// Range = from 0 to 1
335pub fn rand_with_rng<R: Rng>(r: usize, c: usize, rng: &mut R) -> Matrix {
336    let uniform = Uniform::new_inclusive(0f64, 1f64).unwrap();
337    rand_with_dist(r, c, rng, uniform)
338}
339
340/// Rand matrix with specific rng and distribution
341///
342/// # Description
343///
344/// Any range
345pub fn rand_with_dist<T: Into<f64>, R: Rng, D: Distribution<T>>(
346    r: usize,
347    c: usize,
348    rng: &mut R,
349    dist: D,
350) -> Matrix {
351    matrix(rng.sample_iter(dist).take(r * c).collect(), r, c, Row)
352}
353
354// ┌─────────────────────────────────────────────────────────┐
355//  Numpy like non-macro functions
356// └─────────────────────────────────────────────────────────┘
357/// Numpy like logspace
358///
359/// # Examples
360/// ```
361/// use peroxide::fuga::*;
362///
363/// let a = logspace(0, 10, 11, 2);
364/// let b = vec![1f64, 2f64, 4f64, 8f64, 16f64, 32f64, 64f64, 128f64, 256f64, 512f64, 1024f64];
365/// assert_eq!(a, b);
366///
367/// let single = logspace(0f64, 0f64, 1, 10);
368/// assert_eq!(single, vec![1f64]);
369/// ```
370pub fn logspace<S, T, U>(start: S, end: T, length: usize, base: U) -> Vec<f64>
371where
372    S: Into<f64> + Copy,
373    T: Into<f64> + Copy,
374    U: Into<f64> + Copy,
375{
376    let s: f64 = start.into();
377    let e: f64 = end.into();
378    let b: f64 = base.into();
379
380    assert!(e >= s);
381
382    let step: f64 = if length > 1 {
383        (e - s) / (length as f64 - 1f64)
384    } else {
385        0f64
386    };
387
388    let mut v: Vec<f64> = vec![0f64; length];
389
390    for (i, v) in v.iter_mut().enumerate() {
391        *v = b.powf(s + step * (i as f64));
392    }
393    v
394}
395
396/// Numpy like column_stack
397pub fn column_stack(v: &[Vec<f64>]) -> Result<Matrix> {
398    let row = v[0].len();
399    if v.iter().any(|x| x.len() != row) {
400        bail!(ConcatenateError::DifferentLength);
401    }
402    let data = v.iter().flatten().copied().collect();
403    Ok(matrix(data, row, v.len(), Col))
404}
405
406/// Numpy like row_stack
407pub fn row_stack(v: &[Vec<f64>]) -> Result<Matrix> {
408    let col = v[0].len();
409    if v.iter().any(|x| x.len() != col) {
410        bail!(ConcatenateError::DifferentLength);
411    }
412    let data = v.iter().flatten().copied().collect();
413    Ok(matrix(data, v.len(), col, Row))
414}
415
416// ┌─────────────────────────────────────────────────────────┐
417//  Haskell like non-macro functions
418// └─────────────────────────────────────────────────────────┘
419/// Concatenate two vectors into one
420pub fn concat<T: Clone + Copy>(v1: &[T], v2: &[T]) -> Vec<T> {
421    let mut v = v1.to_vec();
422    v.extend_from_slice(v2);
423
424    v
425}
426
427/// Concatenate a value and vector
428pub fn cat<T: Clone + Copy + Default>(val: T, vec: &[T]) -> Vec<T> {
429    let mut v = vec![val];
430    v.extend_from_slice(vec);
431
432    v
433}