Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Math parser and evaluator in Rust, capable of partial differentiation, allows the use of custom operators.

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

bertiqwerty/exmex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crate API example workflow license dependency status crates io downloads

Exmex

Exmex is an extendable mathematical expression parser and evaluator. Ease of use, flexibility, and efficient evaluations are its main design goals. Exmex can parse mathematical expressions possibly containing variables and operators. On the one hand, it comes with a list of default operators for floating point values. For differentiable default operators, Exmex can compute partial derivatives. On the other hand, users can define their own operators and work with different data types such as float, integer, bool, or other types that implement Clone, FromStr, Debug, Default.

Parts of Exmex' functionality are accessible from Python via Mexpress.

Installation

Run

cargo add exmex

in your project's directory for the latest relase. If you want to use the newest version of Exmex, add

[dependencies]
# ...
exmex = { git = "https://github.com/bertiqwerty/exmex.git", branch = "main" }

to your Cargo.toml.

Basic Usage

To simply evaluate a string there is

let result = exmex::eval_str::<f64>("e^(2*π-τ)")?;
assert!((result - 1.0).abs() < 1e-12);

where π/PI, τ/TAU, and Euler's number E/e are available as constants. To create an expression with variables that represents a mathematical function you can use any string that does not define an operator or constant and matches r"[a-zA-Zα-ωΑ-Ω_]+[a-zA-Zα-ωΑ-Ω_0-9]*" as in

use exmex::prelude::*;
let expr = exmex::parse::<f64>("2*x^3-4/y")?;

The wildcard-import from prelude makes only the expression-trait Express and its implementation FlatEx, a flattened expression, accessible. To use variables, you do not need to use a context or tell the parser explicitly what variables are. To evaluate the function at, e.g., x=2.0 and y=4.0 you can use

let result = expr.eval(&[2.0, 4.0])?;
assert!((result - 15.0).abs() < 1e-12);

The order of the variables' values passed for evaluation has to match the alphabetical order of the variable names.

Besides predefined operators for floats, you can implement custom operators and use their factory type as generic argument as shown in the following example.

use exmex::prelude::*;
use exmex::{BinOp, MakeOperators, Operator};
ops_factory!(
    BitwiseOpsFactory,
    u32,
    Operator::make_bin(
        "|",
        BinOp {
            apply: |a, b| a | b,
            prio: 0,
            is_commutative: true,
        }
    ),
    Operator::make_unary("!", |a| !a)
);
let expr = FlatEx::<_, BitwiseOpsFactory>::parse("!(a|b)")?;
let result = expr.eval(&[0, 1])?;
assert_eq!(result, u32::MAX - 1);

More involved examples of data types are

Partial Differentiation

To compute partial derivatives of expressions with floating point numbers, you can use the method partial after activating the Exmex-feature partial in the Cargo.toml via

[dependencies]
exmex = { ..., features = ["partial"] }

The result of the method partial is again an expression.

use exmex::prelude::*;
let expr = exmex::parse::<f64>("y*x^2")?;

// d_x
let dexpr_dx = expr.partial(0)?;
assert_eq!(format!("{}", dexpr_dx), "({x}*2.0)*{y}");

// d_xy
let ddexpr_dxy = dexpr_dx.partial(1)?;
assert_eq!(format!("{}", ddexpr_dxy), "{x}*2.0");
let result = ddexpr_dxy.eval(&[2.0, f64::MAX])?;
assert!((result - 4.0).abs() < 1e-12);

// d_xyx
let dddexpr_dxyx = ddexpr_dxy.partial(0)?;
assert_eq!(format!("{}", dddexpr_dxyx), "2.0");
let result = dddexpr_dxyx.eval(&[f64::MAX, f64::MAX])?;
assert!((result - 2.0).abs() < 1e-12);

// all in one
let dddexpr_dxyx_iter = expr.partial_iter([0, 1, 0].iter())?;
assert_eq!(format!("{}", dddexpr_dxyx_iter), "2.0");
let result = dddexpr_dxyx_iter.eval(&[f64::MAX, f64::MAX])?;
assert!((result - 2.0).abs() < 1e-12);

Mixing Scalar Data Types and Float Vectors in one Expression with the Feature value

After activating the Exmex-feature value one can use expressions with data of type Val, inspired by the type Value from the crate Evalexpr. An instance of Val can contain a boolean, an int, a float, or a vector of floats. This way, it is possible to use booleans, ints, floats, and vectors in the same expression. Further, Exmex provides in terms of ValOpsFactory a pre-defined set of operators for Val. See the following example of a Python-like if-else-operator.

use exmex::{Express, Val};
let expr = exmex::parse_val::<i32, f64>("0 if b < c else 1.2")?;
let res = expr.eval(&[Val::Float(34.0), Val::Int(21)])?.to_float()?;
assert!((res - 1.2).abs() < 1e-12);

See the Val-docs for an example containing vectors.

If both partial and value are activated, partial derivatives can be computed for expressions of types such as FlatExVal<i32, f64>. This is currently not supported for vectors.

use exmex::{Differentiate, Express, Val};
let expr = exmex::parse_val::<i32, f64>("3*x if x > 1 else x^2")?;
let deri = expr.partial(0)?;
let res = deri.eval(&[Val::Float(1.0)])?.to_float()?;
assert!((res - 2.0).abs() < 1e-12);
let res = deri.eval(&[Val::Float(7.0)])?.to_float()?;
assert!((res - 3.0).abs() < 1e-12);

Serialization and Deserialization

To use serde activate the feature serde.

Documentation

More features and examples including integer data types and boolean literals can be found for the documentation release under docs.rs/exmex/ or generated via

cargo doc --all-features --no-deps

Benchmarks

Exmex is the fastest library for evaluating mathematical expressions I am aware of. The parsing speed on the other hand is slower than the one of many other libraries. Benchmarks can be found in Exmex Benchmarks.

License

You as library user can select between MIT and Apache 2.0.

About

Math parser and evaluator in Rust, capable of partial differentiation, allows the use of custom operators.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages