1use rhai::plugin::*;
2
3#[export_module]
4pub mod matrix_functions {
5 #[cfg(feature = "nalgebra")]
6 use crate::matrix::RhaiVector;
7 use crate::matrix::{numeric_vector_data, RhaiMatrix};
8 use crate::validation_functions::{is_column_vector, is_row_vector};
9 use crate::{
10 array_to_vec_float, if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do,
11 if_matrix_convert_to_vec_array_and_do,
12 };
13 #[cfg(feature = "nalgebra")]
14 use crate::{if_matrices_and_compatible_convert_to_vec_array_and_do, FOIL};
15 #[cfg(feature = "nalgebra")]
16 use nalgebralib::DMatrix;
17 use rhai::{Array, Dynamic, EvalAltResult, Map, Position, FLOAT, INT};
18 use std::collections::BTreeMap;
19
20 #[rhai_fn(name = "row", return_raw)]
25 pub fn row_from_array(values: Array) -> Result<Array, Box<EvalAltResult>> {
26 Ok(RhaiMatrix::row_vector(numeric_vector_data(&values)?).to_array())
27 }
28
29 #[rhai_fn(name = "col", return_raw)]
34 pub fn col_from_array(values: Array) -> Result<Array, Box<EvalAltResult>> {
35 Ok(RhaiMatrix::column_vector(numeric_vector_data(&values)?).to_array())
36 }
37
38 #[rhai_fn(name = "mat", return_raw)]
44 pub fn mat_from_array(values: Array) -> Result<Array, Box<EvalAltResult>> {
45 if crate::matrix::matrix_dimensions(&values).is_none() {
46 return Err(EvalAltResult::ErrorArithmetic(
47 "mat expects nonempty row arrays of equal length".into(),
48 Position::NONE,
49 )
50 .into());
51 }
52 for row in &values {
53 numeric_vector_data(&row.clone().into_array().unwrap())?;
54 }
55 Ok(values)
56 }
57
58 #[rhai_fn(name = "dot", return_raw)]
69 pub fn dot(left: Array, right: Array) -> Result<FLOAT, Box<EvalAltResult>> {
70 let mut left = numeric_vector_data(&left)?;
71 let mut right = numeric_vector_data(&right)?;
72 if left.len() != right.len() {
73 return Err(EvalAltResult::ErrorArithmetic(
74 "dot expects vectors of the same length".into(),
75 Position::NONE,
76 )
77 .into());
78 }
79 Ok(array_to_vec_float(&mut left)
80 .iter()
81 .zip(array_to_vec_float(&mut right))
82 .map(|(a, b)| a * b)
83 .sum())
84 }
85
86 #[cfg(feature = "nalgebra")]
107 #[rhai_fn(name = "inv", return_raw, pure)]
108 pub fn invert_matrix(matrix: &mut Array) -> Result<Array, Box<EvalAltResult>> {
109 let dm = RhaiMatrix::from_array(matrix.clone()).to_dmatrix()?;
110 dm.try_inverse()
111 .map(|m| RhaiMatrix::from_dmatrix(&m).to_array())
112 .ok_or_else(|| {
113 EvalAltResult::ErrorArithmetic(
114 "Matrix cannot be inverted".to_string(),
115 Position::NONE,
116 )
117 .into()
118 })
119 }
120
121 #[cfg(feature = "nalgebra")]
136 #[rhai_fn(name = "eigs", return_raw, pure)]
137 pub fn matrix_eigs_alt(matrix: &mut Array) -> Result<Map, Box<EvalAltResult>> {
138 let dm = RhaiMatrix::from_array(matrix.clone()).to_dmatrix()?;
139
140 let dms = dm.shape().1;
141
142 let eigenvalues = dm.complex_eigenvalues();
143
144 let mut imaginary_values = vec![Dynamic::from_float(1.0); 0];
145 let mut real_values = vec![Dynamic::from_float(1.0); 0];
146 let mut residuals = vec![Dynamic::from_float(1.0); 0];
147 let mut eigenvectors = DMatrix::from_element(dms, 0, 0.0);
148 for (idx, ev) in eigenvalues.iter().enumerate() {
149 imaginary_values.push(Dynamic::from_float(ev.im));
150 real_values.push(Dynamic::from_float(ev.re));
151
152 let mut a = dm.clone() - DMatrix::from_diagonal_element(dms, dms, ev.re);
153 a = a.insert_column(0, 0.0);
154 a = a.insert_row(0, 0.0);
155 a[(0, idx + 1)] = 1.0;
156 let mut b = DMatrix::from_element(dms + 1, 1, 0.0);
157 b[(0, 0)] = 1.0;
158 let eigenvector = a
159 .svd(true, true)
160 .solve(&b, 1e-10)
161 .unwrap()
162 .remove_rows(0, 1)
163 .normalize();
164
165 residuals.push(Dynamic::from_float(
166 (dm.clone() * eigenvector.clone() - ev.re * eigenvector.clone()).amax(),
167 ));
168
169 eigenvectors.extend(eigenvector.column_iter());
170 }
171
172 let mut result = BTreeMap::new();
173 let mut vid = smartstring::SmartString::new();
174 vid.push_str("eigenvectors");
175 result.insert(
176 vid,
177 Dynamic::from_array(RhaiMatrix::from_dmatrix(&eigenvectors).to_array()),
178 );
179 let mut did = smartstring::SmartString::new();
180 did.push_str("real_eigenvalues");
181 result.insert(did, Dynamic::from_array(real_values));
182 let mut eid = smartstring::SmartString::new();
183 eid.push_str("imaginary_eigenvalues");
184 result.insert(eid, Dynamic::from_array(imaginary_values));
185 let mut rid = smartstring::SmartString::new();
186 rid.push_str("residuals");
187 result.insert(rid, Dynamic::from_array(residuals));
188
189 Ok(result)
190 }
191
192 #[cfg(feature = "nalgebra")]
199 #[rhai_fn(name = "svd", return_raw, pure)]
200 pub fn svd_decomp(matrix: &mut Array) -> Result<Map, Box<EvalAltResult>> {
201 let dm = RhaiMatrix::from_array(matrix.clone()).to_dmatrix()?;
202 let svd = nalgebralib::linalg::SVD::new(dm, true, true);
203
204 let mut result = BTreeMap::new();
205 let mut u_key = smartstring::SmartString::new();
206 u_key.push_str("u");
207 match svd.u {
208 Some(u) => result.insert(
209 u_key,
210 Dynamic::from_array(RhaiMatrix::from_dmatrix(&u).to_array()),
211 ),
212 None => {
213 return Err(EvalAltResult::ErrorArithmetic(
214 "SVD decomposition cannot be computed for this matrix.".to_string(),
215 Position::NONE,
216 )
217 .into());
218 }
219 };
220
221 let mut v_key = smartstring::SmartString::new();
222 v_key.push_str("v");
223 match svd.v_t {
224 Some(v) => result.insert(
225 v_key,
226 Dynamic::from_array(RhaiMatrix::from_dmatrix(&v).to_array()),
227 ),
228 None => {
229 return Err(EvalAltResult::ErrorArithmetic(
230 "SVD decomposition cannot be computed for this matrix.".to_string(),
231 Position::NONE,
232 )
233 .into());
234 }
235 };
236
237 let mut s_key = smartstring::SmartString::new();
238 s_key.push_str("s");
239 result.insert(
240 s_key,
241 Dynamic::from_array(RhaiVector::from_dvector(&svd.singular_values).to_array()),
242 );
243
244 Ok(result)
245 }
246
247 #[cfg(feature = "nalgebra")]
254 #[rhai_fn(name = "qr", return_raw, pure)]
255 pub fn qr_decomp(matrix: &mut Array) -> Result<Map, Box<EvalAltResult>> {
256 let dm = RhaiMatrix::from_array(matrix.clone()).to_dmatrix()?;
257 let qr = nalgebralib::linalg::QR::new(dm);
258
259 let mut result = BTreeMap::new();
260 let mut qid = smartstring::SmartString::new();
261 qid.push_str("q");
262 result.insert(
263 qid,
264 Dynamic::from_array(RhaiMatrix::from_dmatrix(&qr.q()).to_array()),
265 );
266
267 let mut rid = smartstring::SmartString::new();
268 rid.push_str("r");
269 result.insert(
270 rid,
271 Dynamic::from_array(RhaiMatrix::from_dmatrix(&qr.r()).to_array()),
272 );
273
274 Ok(result)
275 }
276
277 #[cfg(feature = "nalgebra")]
284 #[rhai_fn(name = "hessenberg", return_raw, pure)]
285 pub fn hessenberg(matrix: &mut Array) -> Result<Map, Box<EvalAltResult>> {
286 let dm = RhaiMatrix::from_array(matrix.clone()).to_dmatrix()?;
287 let h = nalgebralib::linalg::Hessenberg::new(dm);
288
289 let mut result = BTreeMap::new();
290 let mut hid = smartstring::SmartString::new();
291 hid.push_str("h");
292 result.insert(
293 hid,
294 Dynamic::from_array(RhaiMatrix::from_dmatrix(&h.h()).to_array()),
295 );
296
297 let mut qid = smartstring::SmartString::new();
298 qid.push_str("q");
299 result.insert(
300 qid,
301 Dynamic::from_array(RhaiMatrix::from_dmatrix(&h.q()).to_array()),
302 );
303
304 Ok(result)
305 }
306
307 #[cfg(feature = "nalgebra")]
321 #[rhai_fn(name = "transpose", return_raw)]
322 pub fn transpose(matrix: RhaiMatrix) -> Result<RhaiMatrix, Box<EvalAltResult>> {
323 let mut raw = matrix.clone().to_array();
324 if !raw.is_empty() && matrix_size_by_reference(&mut raw).len() == 1 {
325 return RhaiMatrix::row_vector(raw).transpose();
326 }
327
328 matrix.transpose()
329 }
330
331 #[cfg(feature = "nalgebra")]
333 #[rhai_fn(name = "transpose", return_raw)]
334 pub fn transpose_from_array(matrix: Array) -> Result<Array, Box<EvalAltResult>> {
335 transpose(RhaiMatrix::from_array(matrix)).map(RhaiMatrix::to_array)
336 }
337
338 #[rhai_fn(name = "size", pure)]
348 pub fn matrix_size_by_reference(matrix: &mut Array) -> Array {
349 let mut new_matrix = matrix.clone();
350
351 let mut shape = vec![Dynamic::from_int(new_matrix.len() as INT)];
352 while let Some(first) = new_matrix.first() {
353 if first.is_array() {
354 new_matrix = first.clone().into_array().unwrap();
355 shape.push(Dynamic::from_int(new_matrix.len() as INT));
356 } else {
357 break;
358 }
359 }
360
361 shape
362 }
363
364 #[rhai_fn(name = "ndims")]
371 pub fn ndims_by_reference(matrix: &mut Array) -> INT {
372 matrix_size_by_reference(matrix).len() as INT
373 }
374
375 #[rhai_fn(name = "numel", pure)]
387 pub fn numel_by_reference(matrix: &mut Array) -> INT {
388 flatten(matrix).len() as INT
389 }
390
391 #[rhai_fn(name = "nnz", pure)]
403 pub fn nnz_by_reference(matrix: &mut Array) -> INT {
404 array_to_vec_float(&mut flatten(matrix))
405 .iter()
406 .filter(|&n| *n > 0.0)
407 .count() as INT
408 }
409
410 #[cfg(feature = "io")]
411 pub mod read_write {
412 use polars::prelude::{CsvReadOptions, DataType, SerReader};
413 use rhai::{Array, Dynamic, EvalAltResult, ImmutableString, FLOAT};
414
415 #[rhai_fn(name = "read_matrix", return_raw)]
421 pub fn read_matrix(file_path: ImmutableString) -> Result<Array, Box<EvalAltResult>> {
422 fn transpose_internal<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>> {
424 assert!(!v.is_empty());
425 let len = v[0].len();
426 let mut iters: Vec<_> = v.into_iter().map(|n| n.into_iter()).collect();
427 (0..len)
428 .map(|_| {
429 iters
430 .iter_mut()
431 .map(|n| n.next().unwrap())
432 .collect::<Vec<T>>()
433 })
434 .collect()
435 }
436
437 let file_path_as_str = file_path.as_str();
438
439 let path_is_url = url::Url::parse(file_path_as_str);
441
442 match path_is_url {
443 Err(_) => {
444 let x = CsvReadOptions::default()
445 .with_has_header(
446 csv_sniffer::Sniffer::new()
447 .sniff_path(file_path_as_str)
448 .map_err(|err| {
449 EvalAltResult::ErrorSystem(
450 format!("Cannot sniff file: {file_path_as_str}"),
451 err.into(),
452 )
453 })?
454 .dialect
455 .header
456 .has_header_row,
457 )
458 .try_into_reader_with_file_path(Some(file_path_as_str.into()))
459 .map_err(|err| {
460 EvalAltResult::ErrorSystem(
461 format!("Cannot read file as CSV: {file_path_as_str}"),
462 err.into(),
463 )
464 })?
465 .finish()
466 .map_err(|err| {
467 EvalAltResult::ErrorSystem(
468 format!("Cannot read file: {file_path_as_str}"),
469 err.into(),
470 )
471 })?
472 .drop_nulls::<String>(None)
473 .map_err(|err| {
474 EvalAltResult::ErrorSystem(
475 format!("Cannot remove null values from file: {file_path_as_str}"),
476 err.into(),
477 )
478 })?;
479
480 let mut final_output = vec![];
482 for series in x.get_columns() {
483 let col: Vec<FLOAT> = series
484 .cast(&DataType::Float64)
485 .map_err(|err| {
486 EvalAltResult::ErrorArithmetic(
487 format!("Data cannot be cast to FLOAT: {err}"),
488 rhai::Position::NONE,
489 )
490 })?
491 .f64()
492 .unwrap()
493 .into_no_null_iter()
494 .map(|el| el as FLOAT)
495 .collect();
496 final_output.push(col);
497 }
498
499 final_output = transpose_internal(final_output);
500
501 let matrix_as_array = final_output
502 .into_iter()
503 .map(|x| {
504 let mut y = vec![];
505 for el in &x {
506 y.push(Dynamic::from_float(*el));
507 }
508 Dynamic::from_array(y)
509 })
510 .collect::<Array>();
511
512 Ok(matrix_as_array)
513 }
514 Ok(_) => {
515 if let Ok(_) = url::Url::parse(file_path_as_str) {
516 let file_contents =
517 minreq::get(file_path_as_str).send().map_err(|err| {
518 EvalAltResult::ErrorSystem(
519 format!("Error getting url: {file_path_as_str}"),
520 err.into(),
521 )
522 })?;
523 let temp = temp_file::with_contents(file_contents.as_bytes());
524
525 let temp_file_name: ImmutableString = temp.path().to_str().unwrap().into();
526
527 read_matrix(temp_file_name)
528 } else {
529 EvalAltResult::ErrorRuntime(
530 format!(
531 "The string {file_path_as_str} is not a valid URL or file path",
532 )
533 .into(),
534 rhai::Position::NONE,
535 )
536 .into()
537 }
538 }
539 }
540 }
541 }
542
543 #[rhai_fn(name = "zeros", return_raw)]
574 pub fn zeros_single_input(n: Dynamic) -> Result<Array, Box<EvalAltResult>> {
575 if_int_do_else_if_array_do(
576 n,
577 |n| Ok(zeros_double_input(n, n)),
578 |m| {
579 if m.len() == 2 {
580 Ok(zeros_double_input(
581 m[0].as_int().unwrap(),
582 m[1].as_int().unwrap(),
583 ))
584 } else if m.len() > 2 {
585 let l = m.remove(0);
586 Ok(vec![
587 Dynamic::from_array(
588 zeros_single_input(Dynamic::from_array(m.to_vec())).unwrap()
589 );
590 l.as_int().unwrap() as usize
591 ])
592 } else {
593 Ok(vec![Dynamic::FLOAT_ZERO; m[0].as_int().unwrap() as usize])
594 }
595 },
596 )
597 }
598
599 #[rhai_fn(name = "zeros")]
607 pub fn zeros_double_input(nx: INT, ny: INT) -> Array {
608 let mut output = vec![];
609 for _ in 0..nx {
610 output.push(Dynamic::from_array(vec![Dynamic::FLOAT_ZERO; ny as usize]))
611 }
612 output
613 }
614
615 #[rhai_fn(name = "ones", return_raw)]
646 pub fn ones_single_input(n: Dynamic) -> Result<Array, Box<EvalAltResult>> {
647 crate::if_int_do_else_if_array_do(
648 n,
649 |n| Ok(ones_double_input(n, n)),
650 |m| {
651 if m.len() == 2 {
652 Ok(ones_double_input(
653 m[0].as_int().unwrap(),
654 m[1].as_int().unwrap(),
655 ))
656 } else if m.len() > 2 {
657 let l = m.remove(0);
658 Ok(vec![
659 Dynamic::from_array(
660 ones_single_input(Dynamic::from_array(m.to_vec())).unwrap()
661 );
662 l.as_int().unwrap() as usize
663 ])
664 } else {
665 Ok(vec![Dynamic::FLOAT_ONE; m[0].as_int().unwrap() as usize])
666 }
667 },
668 )
669 }
670
671 #[rhai_fn(name = "ones")]
679 pub fn ones_double_input(nx: INT, ny: INT) -> Array {
680 let mut output = vec![];
681 for _ in 0..nx {
682 output.push(Dynamic::from_array(vec![Dynamic::FLOAT_ONE; ny as usize]))
683 }
684 output
685 }
686
687 #[cfg(feature = "rand")]
698 #[rhai_fn(name = "rand", return_raw)]
699 pub fn rand_single_input(n: Dynamic) -> Result<Array, Box<EvalAltResult>> {
700 crate::if_int_do_else_if_array_do(
701 n,
702 |n| Ok(rand_double_input(n, n)),
703 |m| {
704 if m.len() == 2 {
705 Ok(rand_double_input(
706 m[0].as_int().unwrap(),
707 m[1].as_int().unwrap(),
708 ))
709 } else if m.len() > 2 {
710 let l = m.remove(0);
711 Ok(vec![
712 Dynamic::from_array(
713 rand_single_input(Dynamic::from_array(m.to_vec())).unwrap()
714 );
715 l.as_int().unwrap() as usize
716 ])
717 } else {
718 Ok(rand_double_input(1, m[0].as_int().unwrap())[0]
719 .clone()
720 .into_array()
721 .unwrap())
722 }
723 },
724 )
725 }
726
727 #[cfg(feature = "rand")]
734 #[rhai_fn(name = "rand")]
735 pub fn rand_double_input(nx: INT, ny: INT) -> Array {
736 let mut output = vec![];
737 for _ in 0..nx {
738 let mut row = vec![];
739 for _ in 0..ny {
740 row.push(Dynamic::from_float(crate::misc_functions::rand_float()));
741 }
742 output.push(Dynamic::from_array(row))
743 }
744 output
745 }
746
747 #[rhai_fn(name = "eye", return_raw)]
765 pub fn eye_single_input(n: Dynamic) -> Result<Array, Box<EvalAltResult>> {
766 fn parse_eye_dimension(value: &Dynamic) -> Result<INT, Box<EvalAltResult>> {
767 value.as_int().map_err(|_| {
768 EvalAltResult::ErrorMismatchDataType(
769 "Size vector for eye must contain integers".to_string(),
770 String::new(),
771 Position::NONE,
772 )
773 .into()
774 })
775 }
776
777 if_int_do_else_if_array_do(
778 n,
779 |n| Ok(eye_double_input(n, n)),
780 |m| match m.len() {
781 1 => {
782 let size = parse_eye_dimension(&m[0])?;
783 Ok(eye_double_input(size, size))
784 }
785 2 => {
786 let rows = parse_eye_dimension(&m[0])?;
787 let cols = parse_eye_dimension(&m[1])?;
788 Ok(eye_double_input(rows, cols))
789 }
790 _ => Err(EvalAltResult::ErrorMismatchDataType(
791 "Cannot create an identity matrix with more than 2 dimensions".to_string(),
792 String::new(),
793 Position::NONE,
794 )
795 .into()),
796 },
797 )
798 }
799
800 #[rhai_fn(name = "eye")]
808 pub fn eye_double_input(nx: INT, ny: INT) -> Array {
809 let mut output = vec![];
810 for i in 0..nx {
811 let mut row = vec![];
812 for j in 0..ny {
813 if i == j {
814 row.push(Dynamic::FLOAT_ONE);
815 } else {
816 row.push(Dynamic::FLOAT_ZERO);
817 }
818 }
819 output.push(Dynamic::from_array(row))
820 }
821 output
822 }
823
824 #[rhai_fn(name = "flatten", pure)]
836 pub fn flatten(matrix: &mut Array) -> Array {
837 let mut flat: Array = vec![];
838 for el in matrix {
839 if el.is_array() {
840 flat.extend(flatten(&mut el.clone().into_array().unwrap()))
841 } else {
842 flat.push(el.clone());
843 }
844 }
845 flat
846 }
847
848 #[rhai_fn(name = "fliplr", return_raw)]
856 pub fn fliplr(matrix: &mut Array) -> Result<Array, Box<EvalAltResult>> {
857 if_matrix_convert_to_vec_array_and_do(matrix, |matrix_as_vec| {
858 let w = matrix_as_vec[0].len();
859 let h = matrix_as_vec.len();
860
861 let mut out = vec![];
863 for idx in 0..h {
864 let mut new_row = vec![];
865 for jdx in 0..w {
866 new_row.push(matrix_as_vec[idx][w - jdx - 1].clone());
867 }
868 out.push(Dynamic::from_array(new_row));
869 }
870 Ok(out)
871 })
872 }
873
874 #[rhai_fn(name = "flipud", return_raw)]
882 pub fn flipud(matrix: &mut Array) -> Result<Array, Box<EvalAltResult>> {
883 if_matrix_convert_to_vec_array_and_do(matrix, |matrix_as_vec| {
884 let w = matrix_as_vec[0].len();
885 let h = matrix_as_vec.len();
886
887 let mut out = vec![];
889 for idx in 0..h {
890 let mut new_row = vec![];
891 for jdx in 0..w {
892 new_row.push(matrix_as_vec[h - idx - 1][jdx].clone());
893 }
894 out.push(Dynamic::from_array(new_row));
895 }
896 Ok(out)
897 })
898 }
899
900 #[rhai_fn(name = "rot90", return_raw)]
908 pub fn rot90_once(matrix: &mut Array) -> Result<Array, Box<EvalAltResult>> {
909 if_matrix_convert_to_vec_array_and_do(matrix, |matrix_as_vec| {
910 let w = matrix_as_vec[0].len();
911 let h = matrix_as_vec.len();
912
913 let mut out = vec![];
915 for idx in 0..w {
916 let mut new_row = vec![];
917 for jdx in 0..h {
918 new_row.push(matrix_as_vec[jdx][w - idx - 1].clone());
919 }
920
921 out.push(Dynamic::from_array(new_row));
922 }
923 Ok(out)
924 })
925 }
926
927 #[rhai_fn(name = "rot90", return_raw)]
935 pub fn rot90_ktimes(matrix: &mut Array, k: INT) -> Result<Array, Box<EvalAltResult>> {
936 if k <= 0 {
937 return Ok(matrix.clone());
938 }
939
940 let mut result = matrix;
941 let mut result_base = Array::new();
942
943 for _ in 0..k {
944 result_base = rot90_once(result)?;
945 result = &mut result_base;
946 }
947
948 Ok(result_base)
949 }
950
951 #[cfg(feature = "nalgebra")]
959 #[rhai_fn(name = "mtimes", return_raw)]
960 pub fn mtimes(matrix1: Array, matrix2: Array) -> Result<Array, Box<EvalAltResult>> {
961 if_matrices_and_compatible_convert_to_vec_array_and_do(
962 FOIL::Inside,
963 &mut matrix1.clone(),
964 &mut matrix2.clone(),
965 |matrix_as_vec1, matrix_as_vec2| {
966 let arr1: Array = matrix_as_vec1
967 .into_iter()
968 .map(Dynamic::from_array)
969 .collect();
970 let arr2: Array = matrix_as_vec2
971 .into_iter()
972 .map(Dynamic::from_array)
973 .collect();
974 let dm1 = RhaiMatrix::from_array(arr1).to_dmatrix()?;
975 let dm2 = RhaiMatrix::from_array(arr2).to_dmatrix()?;
976 let mat = dm1 * dm2;
977 Ok(RhaiMatrix::from_dmatrix(&mat).to_array())
978 },
979 )
980 }
981
982 #[cfg(feature = "nalgebra")]
990 #[rhai_fn(name = "horzcat", return_raw)]
991 pub fn horzcat(
992 matrix1: RhaiMatrix,
993 matrix2: RhaiMatrix,
994 ) -> Result<RhaiMatrix, Box<EvalAltResult>> {
995 matrix1.concat_h(&matrix2)
996 }
997
998 #[cfg(feature = "nalgebra")]
999 #[rhai_fn(name = "horzcat", return_raw)]
1000 pub fn horzcat_from_array(matrix1: Array, matrix2: Array) -> Result<Array, Box<EvalAltResult>> {
1001 horzcat(
1002 RhaiMatrix::from_array(matrix1),
1003 RhaiMatrix::from_array(matrix2),
1004 )
1005 .map(RhaiMatrix::to_array)
1006 }
1007
1008 #[cfg(feature = "nalgebra")]
1016 #[rhai_fn(name = "vertcat", return_raw)]
1017 pub fn vertcat(
1018 matrix1: RhaiMatrix,
1019 matrix2: RhaiMatrix,
1020 ) -> Result<RhaiMatrix, Box<EvalAltResult>> {
1021 matrix1.concat_v(&matrix2)
1022 }
1023
1024 #[cfg(feature = "nalgebra")]
1025 #[rhai_fn(name = "vertcat", return_raw)]
1026 pub fn vertcat_from_array(matrix1: Array, matrix2: Array) -> Result<Array, Box<EvalAltResult>> {
1027 vertcat(
1028 RhaiMatrix::from_array(matrix1),
1029 RhaiMatrix::from_array(matrix2),
1030 )
1031 .map(RhaiMatrix::to_array)
1032 }
1033
1034 #[rhai_fn(name = "diag", return_raw)]
1053 pub fn diag(matrix: Array) -> Result<Array, Box<EvalAltResult>> {
1054 let dims = ndims_by_reference(&mut matrix.clone());
1055 if dims == 2 {
1056 let mut candidate_for_row = matrix.clone();
1057 let mut candidate_for_col = matrix.clone();
1058 if is_row_vector(&mut candidate_for_row) || is_column_vector(&mut candidate_for_col) {
1059 let mut flattened_vector = matrix.clone();
1060 let vector = flatten(&mut flattened_vector);
1061 return Ok(diagonal_matrix_from_vector(vector));
1062 }
1063
1064 let matrix_as_vec = matrix
1065 .into_iter()
1066 .map(|x| x.into_array().unwrap())
1067 .collect::<Vec<Array>>();
1068
1069 if matrix_as_vec.is_empty() {
1070 return Ok(vec![]);
1071 }
1072
1073 let cols = matrix_as_vec[0].len();
1074 let diag_len = matrix_as_vec.len().min(cols);
1075 let mut out = vec![];
1076 for i in 0..diag_len {
1077 out.push(matrix_as_vec[i][i].clone());
1078 }
1079
1080 Ok(out)
1081 } else if dims == 1 {
1082 Ok(diagonal_matrix_from_vector(matrix))
1083 } else {
1084 Err(EvalAltResult::ErrorArithmetic(
1085 "Argument must be a 2-D matrix (to extract the diagonal) or a 1-D array (to create a matrix with that diagonal".to_string(),
1086 Position::NONE,
1087 )
1088 .into())
1089 }
1090 }
1091
1092 fn diagonal_matrix_from_vector(vector: Array) -> Array {
1093 let mut out = vec![];
1094 for idx in 0..vector.len() {
1095 let mut new_row = vec![];
1096 for jdx in 0..vector.len() {
1097 if idx == jdx {
1098 new_row.push(vector[idx].clone());
1099 } else if vector[idx].is_int() {
1100 new_row.push(Dynamic::ZERO);
1101 } else {
1102 new_row.push(Dynamic::FLOAT_ZERO);
1103 }
1104 }
1105 out.push(Dynamic::from_array(new_row));
1106 }
1107 out
1108 }
1109
1110 #[cfg(feature = "nalgebra")]
1117 #[rhai_fn(name = "repmat", return_raw)]
1118 pub fn repmat(matrix: RhaiMatrix, nx: INT, ny: INT) -> Result<RhaiMatrix, Box<EvalAltResult>> {
1119 let oriented = matrix
1120 .as_column()
1121 .or_else(|| matrix.as_row())
1122 .unwrap_or(matrix);
1123 let dm = oriented.to_dmatrix()?;
1124 let nx = if nx < 1 { 1 } else { nx as usize };
1125 let ny = if ny < 1 { 1 } else { ny as usize };
1126 let mat = DMatrix::from_fn(dm.nrows() * nx, dm.ncols() * ny, |i, j| {
1127 dm[(i % dm.nrows(), j % dm.ncols())]
1128 });
1129 Ok(RhaiMatrix::from_dmatrix(&mat))
1130 }
1131
1132 #[cfg(feature = "nalgebra")]
1133 #[rhai_fn(name = "repmat", return_raw)]
1134 pub fn repmat_from_array(matrix: Array, nx: INT, ny: INT) -> Result<Array, Box<EvalAltResult>> {
1135 repmat(RhaiMatrix::from_array(matrix), nx, ny).map(RhaiMatrix::to_array)
1136 }
1137
1138 #[rhai_fn(name = "meshgrid", return_raw)]
1156 pub fn meshgrid(x: Array, y: Array) -> Result<Map, Box<EvalAltResult>> {
1157 if_list_do(&mut x.clone(), |x| {
1158 if_list_do(&mut y.clone(), |y| {
1159 let nx = x.len();
1160 let ny = y.len();
1161 let x_dyn: Array = (0..ny).map(|_| Dynamic::from_array(x.to_vec())).collect();
1162 let y_dyn: Array = y
1163 .iter()
1164 .map(|value| {
1165 let y_row: Array = (0..nx).map(|_| value.clone()).collect();
1166 Dynamic::from_array(y_row)
1167 })
1168 .collect();
1169
1170 let mut result = BTreeMap::new();
1171 let mut xid = smartstring::SmartString::new();
1172 xid.push_str("x");
1173 let mut yid = smartstring::SmartString::new();
1174 yid.push_str("y");
1175 result.insert(xid, Dynamic::from_array(x_dyn));
1176 result.insert(yid, Dynamic::from_array(y_dyn));
1177 Ok(result)
1178 })
1179 })
1180 }
1181
1182 #[rhai_fn(name = "linspace", return_raw)]
1188 pub fn linspace(x1: Dynamic, x2: Dynamic, n: INT) -> Result<Array, Box<EvalAltResult>> {
1189 if_int_convert_to_float_and_do(x1, |new_x1| {
1190 if_int_convert_to_float_and_do(x2.clone(), |new_x2| {
1191 let new_n = n as FLOAT;
1192
1193 let mut arr = vec![Dynamic::from_float(new_x1)];
1194 let mut counter = new_x1;
1195 let interval = (new_x2 - new_x1) / (new_n - 1.0);
1196 for _ in 0..(n - 2) {
1197 counter += interval;
1198 arr.push(Dynamic::from_float(counter));
1199 }
1200 arr.push(Dynamic::from_float(new_x2));
1201 Ok(arr)
1202 })
1203 })
1204 }
1205
1206 #[rhai_fn(name = "logspace", return_raw)]
1212 pub fn logspace(a: Dynamic, b: Dynamic, n: INT) -> Result<Array, Box<EvalAltResult>> {
1213 linspace(a, b, n).map(|arr| {
1214 arr.iter()
1215 .map(|e| Dynamic::from_float((10 as FLOAT).powf(e.as_float().unwrap())))
1216 .collect::<Array>()
1217 })
1218 }
1219}