Skip to content

Commit

Permalink
Merge pull request #20 from cmccomb/master
Browse files Browse the repository at this point in the history
Preparing v0.2.2
  • Loading branch information
cmccomb authored Jan 26, 2025
2 parents d344ab3 + c34ca7d commit eb805ad
Show file tree
Hide file tree
Showing 10 changed files with 812 additions and 116 deletions.
36 changes: 18 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rhai-sci"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
authors = ["Chris McComb <[email protected]>"]
description = "Scientific computing in the Rhai scripting language"
Expand All @@ -22,29 +22,29 @@ rand = ["randlib"]

[dependencies]
rhai = ">=1.8.0"
nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" }
polars = { version = "0.27.2", optional = true }
url = { version = "2.2.2", optional = true }
temp-file = { version = "0.1.6", optional = true }
nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" }
polars = { version = "0.45.1", optional = true }
url = { version = ">=2.0.0", optional = true }
temp-file = { version = "0.1.9", optional = true }
csv-sniffer = { version = "0.3.1", optional = true }
minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true }
randlib = { version = "0.8", optional = true, package = "rand" }
smartstring = "1.0.1"
minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true }
randlib = { version = "0.8.5", optional = true, package = "rand" }
smartstring = ">=1.0"
linregress = { version = "0.5.0", optional = true }

[build-dependencies]
rhai = ">=1.8.0"
nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" }
polars = { version = "0.27.2", optional = true }
url = { version = "2.2.2", optional = true }
temp-file = { version = "0.1.6", optional = true }
nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" }
polars = { version = "0.45.1", optional = true }
url = { version = ">=2.0.0", optional = true }
temp-file = { version = "0.1.9", optional = true }
csv-sniffer = { version = "0.3.1", optional = true }
minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true }
randlib = { version = "0.8", optional = true, package = "rand" }
serde_json = "1.0.82"
serde = "1.0.140"
smartstring = "1.0.1"
linregress = { version = "0.5.0", optional = true }
minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true }
randlib = { version = "0.8.5", optional = true, package = "rand" }
serde_json = ">=1.0.0"
serde = ">=1.0.0"
smartstring = ">=1.0.0"
linregress = { version = "0.5.4", optional = true }

[package.metadata.docs.rs]
all-features = true
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,22 @@

# About `rhai-sci`

This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language, inspired by languages
like MATLAB, Octave, and R. For a complete API reference, check [the docs](https://docs.rs/rhai-sci).
This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language,
inspired by languages like MATLAB, Octave, and R. For a complete API reference,
check [the docs](https://docs.rs/rhai-sci).

# Install

To use the latest released version of `rhai-sci`, add this to your `Cargo.toml`:

```toml
rhai-sci = "0.2.1"
```

To use the bleeding edge instead, add this:

```toml
rhai-sci = { git = "https://github.com/cmccomb/rhai-sci" }
rhai-sci = "0.2.2"
```

# Usage

Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you only need:
Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you
only need:

```rust
use rhai::INT;
Expand All @@ -49,9 +45,9 @@ let value = engine.eval::<INT>("argmin([43, 42, -500])").unwrap();

# Features

| Feature | Default | Description |
| ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. |
| `io` | Enabled | Enables the [`read_matrix`](#read_matrixfile_path-string---array) function but pulls in several additional dependencies (`polars`, `url`, `temp-file`, `csv-sniffer`, `minreq`). |
| `nalgebra` | Enabled | Enables several functions ([`regress`](#regressx-array-y-array---map), [`inv`](#invmatrix-array---array), [`mtimes`](#mtimesmatrix1-array-matrix2-array---array), [`horzcat`](#horzcatmatrix1-array-matrix2-array---array), [`vertcat`](#vertcatmatrix1-array-matrix2-array---array), [`repmat`](#repmatmatrix-array-nx-i64-ny-i64---array), [`svd`](#svdmatrix-array---map), [`hessenberg`](#hessenbergmatrix-array---map), and [`qr`](#qrmatrix-array---map)) but brings in the `nalgebra` and `linregress` crates. |
| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. |
| Feature | Default | Description |
|------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. |
| `io` | Enabled | Enables the [`read_matrix`](#read_matrixfile_path-string---array) function but pulls in several additional dependencies (`polars`, `url`, `temp-file`, `csv-sniffer`, `minreq`). |
| `nalgebra` | Enabled | Enables several functions ([`regress`](#regressx-array-y-array---map), [`inv`](#invmatrix-array---array), [`mtimes`](#mtimesmatrix1-array-matrix2-array---array), [`horzcat`](#horzcatmatrix1-array-matrix2-array---array), [`vertcat`](#vertcatmatrix1-array-matrix2-array---array), [`repmat`](#repmatmatrix-array-nx-i64-ny-i64---array), [`svd`](#svdmatrix-array---map), [`hessenberg`](#hessenbergmatrix-array---map), and [`qr`](#qrmatrix-array---map)) but brings in the `nalgebra` and `linregress` crates. |
| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. |
4 changes: 3 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ fn main() {
combine_with_exported_module!(&mut lib, "rhai_sci_sets", set_functions);
combine_with_exported_module!(&mut lib, "rhai_sci_moving", moving_functions);
combine_with_exported_module!(&mut lib, "rhai_sci_validate", validation_functions);
combine_with_exported_module!(&mut lib, "rhai_sci_trig", trig_functions);
engine.register_global_module(rhai::Shared::new(lib));

// Extract metadata
Expand All @@ -84,7 +85,7 @@ fn main() {
let function = function.clone();
// Pull out basic info
let name = function.name;
if !name.starts_with("anon") && !name.starts_with("_") {
if !name.starts_with("anon") && !name.starts_with("_") && !name.starts_with("$CONSTANTS$"){
let signature = function
.signature
.replace("Result<", "")
Expand Down Expand Up @@ -219,6 +220,7 @@ mod functions {
include!("src/moving.rs");
include!("src/validate.rs");
include!("src/patterns.rs");
include!("src/trig.rs");
}

#[cfg(feature = "metadata")]
Expand Down
80 changes: 79 additions & 1 deletion src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use rhai::plugin::*;

#[export_module]
pub mod assert_functions {
use rhai::{Dynamic, EvalAltResult, Position};
use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT};

use crate::if_list_convert_to_vec_float_and_do;

/// Assert that a statement is true and throw an error if it is not.
/// ```typescript
Expand Down Expand Up @@ -88,4 +90,80 @@ pub mod assert_functions {
.into())
}
}

/// Assert that two floats are approximately equal (within `eps`) and return an error if they
/// are not.
/// ```typescript
/// assert_approx_eq(2.0, 2.000000000000000001, 1e-10);
/// ```
#[rhai_fn(name = "assert_approx_eq", return_raw)]
pub fn assert_approx_eq(
lhs: FLOAT,
rhs: FLOAT,
eps: FLOAT,
) -> Result<bool, Box<EvalAltResult>> {
if (lhs - rhs).abs() < eps {
Ok(true)
} else {
println!("LHS: {:?}", lhs);
println!("RHS: {:?}", rhs);
Err(EvalAltResult::ErrorArithmetic(
"The left-hand side and right-hand side are not equal".to_string(),
Position::NONE,
)
.into())
}
}

/// Assert that two floats are approximately equal and return an error if they
/// are not. Use the default tolerance of 1e-10 for the comparison.
/// ```typescript
/// assert_approx_eq(2.0, 2.000000000000000001);
/// ```
#[rhai_fn(name = "assert_approx_eq", return_raw)]
pub fn assert_approx_eq_with_default(
lhs: FLOAT,
rhs: FLOAT,
) -> Result<bool, Box<EvalAltResult>> {
assert_approx_eq(lhs, rhs, 1e-10)
}

/// Assert that two arrays are approximately equal (within `eps`) and return an error if they
/// are not.
/// ```typescript
/// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001], 1e-10);
/// ```
#[rhai_fn(name = "assert_approx_eq", return_raw)]
pub fn assert_approx_eq_list(
lhs: Array,
rhs: Array,
eps: FLOAT,
) -> Result<bool, Box<EvalAltResult>> {
if_list_convert_to_vec_float_and_do(&mut rhs.clone(), |rhs_as_vec_float| {
if_list_convert_to_vec_float_and_do(&mut lhs.clone(), |lhs_as_vec_float| {
let mut result = Ok(true);
for i in 0..rhs_as_vec_float.len() {
result = result.and(assert_approx_eq(
lhs_as_vec_float[i],
rhs_as_vec_float[i],
eps,
))
}
result
})
})
}

/// Assert that two arrays are approximately equal and return an error if they
/// are not. Use the default tolerance of 1e-10 for the comparison.
/// ```typescript
/// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001]);
/// ```
#[rhai_fn(name = "assert_approx_eq", return_raw)]
pub fn assert_approx_eq_list_with_default(
lhs: Array,
rhs: Array,
) -> Result<bool, Box<EvalAltResult>> {
assert_approx_eq_list(lhs, rhs, 1e-10)
}
}
25 changes: 14 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@
#![doc = include_str!("../docs/highlight.html")]

mod patterns;
use patterns::*;
pub use patterns::*;
use rhai::{def_package, packages::Package, plugin::*, Engine, EvalAltResult};
mod matrices_and_arrays;
use matrices_and_arrays::matrix_functions;
pub use matrices_and_arrays::matrix_functions;
mod statistics;
use statistics::stats;
pub use statistics::stats;
mod misc;
use misc::misc_functions;
pub use misc::misc_functions;
mod cumulative;
use cumulative::cum_functions;
pub use cumulative::cum_functions;
mod integration_and_differentiation;
use integration_and_differentiation::int_and_diff;
pub use integration_and_differentiation::int_and_diff;
mod assertions;
use assertions::assert_functions;
pub use assertions::assert_functions;
mod constants;
use constants::constant_definitions;
pub use constants::constant_definitions;
mod moving;
use moving::moving_functions;
pub use moving::moving_functions;
mod sets;
use sets::set_functions;
pub use sets::set_functions;
mod validate;
use validate::validation_functions;
pub use validate::validation_functions;
mod trig;
pub use trig::trig_functions;

def_package! {
/// Package for scientific computing
Expand All @@ -43,6 +45,7 @@ def_package! {
combine_with_exported_module!(lib, "rhai_sci_sets", set_functions);
combine_with_exported_module!(lib, "rhai_sci_moving", moving_functions);
combine_with_exported_module!(lib, "rhai_sci_validation", validation_functions);
combine_with_exported_module!(lib, "rhai_sci_trig", trig_functions);
}
}

Expand Down
51 changes: 39 additions & 12 deletions src/matrices_and_arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rhai::plugin::*;
#[export_module]
pub mod matrix_functions {
use crate::{
if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do,
array_to_vec_float, if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do,
if_matrix_convert_to_vec_array_and_do,
};
#[cfg(feature = "nalgebra")]
Expand Down Expand Up @@ -357,9 +357,28 @@ pub mod matrix_functions {
flatten(matrix).len() as INT
}

/// Returns the number of non-zero elements in a matrix, passed by reference.
/// ```typescript
/// let matrix = ones(4, 6);
/// let n = nnz(matrix);
/// assert_eq(n, 24);
/// ```
/// ```typescript
/// let matrix = eye(4);
/// let n = nnz(matrix);
/// assert_eq(n, 4);
/// ```
#[rhai_fn(name = "nnz", pure)]
pub fn nnz_by_reference(matrix: &mut Array) -> INT {
array_to_vec_float(&mut flatten(matrix))
.iter()
.filter(|&n| *n > 0.0)
.count() as INT
}

#[cfg(all(feature = "io"))]
pub mod read_write {
use polars::prelude::{CsvReader, DataType, SerReader};
use polars::prelude::{CsvReadOptions, DataType, SerReader};
use rhai::{Array, Dynamic, EvalAltResult, ImmutableString, FLOAT};

/// Reads a numeric csv file from a url
Expand Down Expand Up @@ -387,11 +406,13 @@ pub mod matrix_functions {

let file_path_as_str = file_path.as_str();

match CsvReader::from_path(file_path_as_str) {
Ok(csv) => {
let x = csv
.infer_schema(Some(10))
.has_header(
// Determine path is url
let path_is_url = url::Url::parse(file_path_as_str);

match path_is_url {
Err(_) => {
let x = CsvReadOptions::default()
.with_has_header(
csv_sniffer::Sniffer::new()
.sniff_path(file_path_as_str)
.map_err(|err| {
Expand All @@ -404,14 +425,21 @@ pub mod matrix_functions {
.header
.has_header_row,
)
.finish()
.try_into_reader_with_file_path(Some(file_path_as_str.into()))
.map_err(|err| {
EvalAltResult::ErrorSystem(
format!("Cannot read file as CSV: {file_path_as_str}"),
err.into(),
)
})?
.drop_nulls(None)
.finish()
.map_err(|err| {
EvalAltResult::ErrorSystem(
format!("Cannot read file: {file_path_as_str}"),
err.into(),
)
})?
.drop_nulls::<String>(None)
.map_err(|err| {
EvalAltResult::ErrorSystem(
format!("Cannot remove null values from file: {file_path_as_str}"),
Expand All @@ -420,7 +448,6 @@ pub mod matrix_functions {
})?;

// Convert into vec of vec

let mut final_output = vec![];
for series in x.get_columns() {
let col: Vec<FLOAT> = series
Expand Down Expand Up @@ -454,7 +481,7 @@ pub mod matrix_functions {

Ok(matrix_as_array)
}
Err(_) => {
Ok(_) => {
if let Ok(_) = url::Url::parse(file_path_as_str) {
let file_contents =
minreq::get(file_path_as_str).send().map_err(|err| {
Expand Down Expand Up @@ -753,7 +780,7 @@ pub mod matrix_functions {
output
}

/// Returns the contents of an multidimensional array as a 1-D array.
/// Returns the contents of a multidimensional array as a 1-D array.
/// ```typescript
/// let matrix = ones(3, 5);
/// let flat = flatten(matrix);
Expand Down
Loading

0 comments on commit eb805ad

Please sign in to comment.