Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b2bfaa7
Exposing internal rust functions to allow re-use for development of r…
cmccomb Feb 9, 2024
d4ebda2
Added nnz
cmccomb Feb 17, 2024
406673e
Adding some documentation for patterns
cmccomb Feb 17, 2024
4ff907d
Added approximate equal assertion
cmccomb Feb 18, 2024
59a08be
Added new trig module
cmccomb Feb 18, 2024
7f332d3
Introduce and use conversion functions
cmccomb Feb 18, 2024
3f314df
Adding asind, acosd, atand
cmccomb Feb 18, 2024
b29d1d5
Adding hypot
cmccomb Feb 18, 2024
ac8e54b
Forget hypot, tis silly
cmccomb Feb 18, 2024
79e31dc
Added full function sets for cosecant, secant, and cotangent
cmccomb Feb 18, 2024
0cec92b
Better documentation for assert_approx_eq
cmccomb Feb 18, 2024
d27ef64
Added degree versions of hyperbolic sin, cos, and tan (along with inv…
cmccomb Feb 18, 2024
7b0a35a
Added degree versions of hyperbolic sin, cos, and tan (along with inv…
cmccomb Feb 18, 2024
2937888
Cleaning up documentation
cmccomb Feb 18, 2024
cd9c01a
Conversion between coordinate systems
cmccomb Feb 18, 2024
130f163
Add approx_equal assertion type for lists
cmccomb Feb 18, 2024
f75d44a
New tests
cmccomb Feb 18, 2024
b823391
Added test for hypot
cmccomb Feb 18, 2024
35f7d30
Tests for polar-cartesian conversions
cmccomb Feb 18, 2024
c95a9a6
Cleanup
cmccomb Feb 18, 2024
a1d2036
Added tests for tan, cos, and sin series.
cmccomb Feb 19, 2024
a6bcf1b
Added tests for csc and cscd
cmccomb Feb 19, 2024
c4c140f
Updated build system for better docs
cmccomb Feb 19, 2024
29d521e
Added infinity
cmccomb Feb 19, 2024
b7e0960
Added acsc tests
cmccomb Feb 19, 2024
6ad636e
Replaced instances of 1.0/0.0 with inf in tests
cmccomb Feb 19, 2024
ef97b1e
So many trig tests
cmccomb Feb 19, 2024
0bf498b
Updated to recent version of dependencies, passing tests
cmccomb Jan 26, 2025
ca79468
A whole bunch of tests for trig functions
cmccomb Jan 26, 2025
c34ca7d
Bumping version number
cmccomb Jan 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <ccmcc2012@gmail.com>"]
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
Loading