MCPcopy Index your code
hub / github.com/Gleb-Zaslavsky/RustedSciThe

github.com/Gleb-Zaslavsky/RustedSciThe @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
9,729 symbols 46,575 edges 468 files 2,304 documented · 24%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

[TOC]

RustedSciThe

is a Rust library for symbolic and numerical computing: parse string expressions in symbolic representation/symbolic function and compute symbolic derivatives or/and transform symbolic expressions into regular Rust functions, compute symbolic Jacobian and solve initial value problems for for stiff ODEs with BDF and Backward Euler methods, non-stiff ODEs and Boundary Value Problem (BVP) using Newton iterations

Content

Motivation

At first, this code was part of the KiThe crate, where it was supposed to serve for constructing analytical Jacobians for solving systems of equations of combustion, chemical kinetics and heat and mass transfer, as well as for displaying analytical expressions, but it soon became clear that it could be useful for a broader circle of users

Features

  • parsing string expressions in symbolic to a symbolic expression/function
  • symbolic/analytical differentiation of symbolic expressions/functions
  • compare analytical derivative to a numerical one
  • calculate _vector of partial derivatives
  • transform symbolic expressions/functions (also derivatives) into regular Rust functions
  • calculate symbolic/analytical Jacobian and transform it into functional form
  • Newton-Raphson method with analytical Jacobian
  • Backward Eeuler method with analytical Jacobian
  • Backward Differetiation Formula method (BDF) with analytical Jacobian (direct rewrite of python BDF solver from SciPy library)
  • classical methods for non-stiff equations RK45 and DP
  • Boundary Value Problem for ODE with Newton-Raphson method (several versions available)

Usage

  • parse string expression of multiple arguments to a symbolic representation/function and then differentiate it and "lamdufy" it (transform it into a regular rust function). Compare analytical derivative to a numerical one. Calculate the vector of partials derivatives. Solve IVP and BVP problems.
// FUNCTION OF MULTIPLE VARIABLES
//parse expression from string to symbolic expression
let input = "exp(_x)+log(_y)";  
// here you've got symbolic expression
let parsed_expression = Expr::parse_expression(input);
println!(" parsed_expression {}", parsed_expression);
// turn symbolic expression to a pretty human-readable string
let parsed_function = parsed_expression.sym_to_str("_x");
println!("{}, sym to string: {}  \n",input,  parsed_function);
// return _vec of all arguments
let  all = parsed_expression.all_arguments_are_variables();
println!("all arguments are variables {:?}",all);
let variables = parsed_expression.extract_variables();
println!("variables {:?}",variables);

// differentiate with respect to _x and _y
let df_dx = parsed_expression.diff("_x");
let df_dy = parsed_expression.diff("_y");
println!("df_dx = {}, df_dy = {}", df_dx, df_dy);
//convert symbolic expression to a Rust function and evaluate the function
let args = _vec!["_x","_y"];
let function_of_x_and_y = parsed_expression.lambdify( args );
let f_res = function_of_x_and_y( &[1.0, 2.0] );
println!("f_res = {}", f_res);
// or you dont want to pass arguments you can use lambdify_wrapped, arguments will be found inside function
let function_of_x_and_y = parsed_expression.lambdify_wrapped( );
let f_res = function_of_x_and_y( &[1.0, 2.0] );
println!("f_res2 = {}", f_res);
// evaluate function of 2 or more arguments using linspace for defining vectors of arguments
let start = _vec![ 1.0, 1.0];
let end = _vec![ 2.0, 2.0];
let result = parsed_expression.lamdified_from_linspace(start.clone(), end.clone(), 10); 
println!("evaluated function of 2 arguments = {:?}", result);
 //  find _vector of derivatives with respect to all arguments
let vector_of_derivatives = parsed_expression.diff_multi();
println!("vector_of_derivatives = {:?}, {}", vector_of_derivatives, vector_of_derivatives.len());
// compare numerical and analtical derivatives for a given linspace defined by start, end _values and number of _values.
// max_norm - maximum norm of the difference between numerical and analtical derivatives
let comparsion = parsed_expression.compare_num(start, end, 100, 1e-6);
println!(" result_of compare = {:?}", comparsion);

  • the same for a function of one variable
//  FUNTION OF 1 VARIABLE (processing of them has a slightly easier syntax then for multiple variables)
   // function of 1 argument (1D examples)
   let input = "log(_x)"; 
   let f = Expr::parse_expression(input);
   //convert symbolic expression to a Rust function and evaluate the function
   let f_res = f.lambdify1D()(1.0);
   let df_dx = f.diff("_x");
   println!("df_dx = {}, log(1) = {}", df_dx, f_res);

   let input = "_x+exp(_x)"; 
   let f = Expr::parse_expression(input);
   let f_res = f.lambdify1D()(1.0);
   println!("f_res = {}", f_res);
   let start = 0.0;
   let end = 10 as f64;
   let num_values = 100;
   let max_norm = 1e-6;
   // compare numerical and analtical derivatives for a given linspace defined by start, end _values and number of _values.
   // a norm of the difference between the two of them is returned, and the answer is true if the norm is below max_norm 
   let (norm, _res) = f.compare_num1D("_x", start, end, num_values, max_norm);
   println!("norm = {}, _res = {}", norm, _res);
  • a symbolic function can be defined in a more straightforward way without parsing expression
   // SOME USEFUL FEATURES
    // first define symbolic variables
    let vector_of_symbolic_vars = Expr::Symbols( "a, b, c");
    println!("vector_of_symbolic_vars = {:?}", vector_of_symbolic_vars);
    let (mut a,mut  b, mut c) = (vector_of_symbolic_vars[0].clone(), 
    // consruct symbolic expression
    vector_of_symbolic_vars[1].clone(), vector_of_symbolic_vars[2]. clone());
    let mut symbolic_expression =  a + Expr::exp(b * c);
    println!("symbolic_expression = {:?}", symbolic_expression);
    // if you want to change a variable inti constant:
    let mut expression_with_const =  symbolic_expression.set_variable("a", 1.0);
    println!("expression_with_const = {:?}", expression_with_const);
    let parsed_function = expression_with_const.sym_to_str("a");
    println!("{}, sym to string:",  parsed_function);


  • calculate symbolic jacobian and evaluate it
 // JACOBIAN
      // instance of Jacobian _structure
      let mut Jacobian_instance = Jacobian::new();
      // function of 2 or more arguments 
     let vec_of_expressions = _vec![ "2*_x^3+_y".to_string(), "1.0".to_string()]; 
        // set _vector of functions
       Jacobian_instance.set_funcvecor_from_str(vec_of_expressions);
       // set _vector of variables
     //  Jacobian_instance.set_varvecor_from_str("_x, _y");
      Jacobian_instance.set_variables(_vec!["_x", "_y"]);
       // calculate symbolic jacobian
       Jacobian_instance.calc_jacobian();
       // transform into human...kind of readable form
       Jacobian_instance.readable_jacobian();
       // generate jacobian made of regular rust functions
       Jacobian_instance.jacobian_generate(_vec!["_x", "_y"]);

      println!("Jacobian_instance: functions  {:?}. Variables {:?}", Jacobian_instance.vector_of_functions, Jacobian_instance.vector_of_variables);
       println!("Jacobian_instance: Jacobian  {:?} readable {:?}.", Jacobian_instance.symbolic_jacobian, Jacobian_instance.readable_jacobian);
      for _i in 0.. Jacobian_instance.symbolic_jacobian.len() {
        for j in 0.. Jacobian_instance.symbolic_jacobian[_i].len() {
          println!("Jacobian_instance: Jacobian  {} row  {} colomn {:?}", _i, j, Jacobian_instance.symbolic_jacobian[_i][j]);
        }

      }
      // calculate element of jacobian (just for control)
      let ij_element = Jacobian_instance.calc_ij_element(0, 0,  _vec!["_x", "_y"],_vec![10.0, 2.0]) ;
      println!("ij_element = {:?} \n", ij_element);
      // evaluate jacobian to numerical _values
       Jacobian_instance.evaluate_func_jacobian(&_vec![10.0, 2.0]);
       println!("Jacobian = {:?} \n", Jacobian_instance.evaluated_jacobian);
       // lambdify and evaluate function _vector to numerical _values
      Jacobian_instance. lambdify_and_ealuate_funcvector(_vec!["_x", "_y"], _vec![10.0, 2.0]);
       println!("function _vector = {:?} \n", Jacobian_instance.evaluated_functions);
       // or first lambdify
       Jacobian_instance.lambdify_funcvector(_vec!["_x", "_y"]);
       // then evaluate
       Jacobian_instance.evaluate_funvector_lambdified(_vec![10.0, 2.0]);
       println!("function _vector after evaluate_funvector_lambdified = {:?} \n", Jacobian_instance.evaluated_functions);
       // evaluate jacobian to nalgebra matrix format
       Jacobian_instance.evaluate_func_jacobian_DMatrix(_vec![10.0, 2.0]);
       println!("Jacobian_DMatrix = {:?} \n", Jacobian_instance.evaluated_jacobian_DMatrix);
       // evaluate function _vector to nalgebra matrix format
       Jacobian_instance.evaluate_funvector_lambdified_DVector(_vec![10.0, 2.0]);
       println!("function _vector after evaluate_funvector_lambdified_DMatrix = {:?} \n", Jacobian_instance.evaluated_functions_DVector);
  • set and calculate the system of (nonlinear) algebraic equations
//use the shortest way to solve system of equations
    // first define system of equations and initial guess
    let mut NR_instanse = NR::new();
    let vec_of_expressions = _vec![ "_x^2+_y^2-10".to_string(), "_x-_y-4".to_string()]; 
    let initial_guess = _vec![1.0, 1.0];
    // solve
    NR_instanse.eq_generate_from_str(vec_of_expressions,initial_guess, 1e-6, 100, 1e-6);
    NR_instanse.solve();
    println!("result = {:?} \n", NR_instanse.get_result().unwrap());
    // or more verbose way...
    // first define system of equations

    let vec_of_expressions = _vec![ "_x^2+_y^2-10".to_string(), "_x-_y-4".to_string()]; 
    let mut Jacobian_instance = Jacobian::new();
     Jacobian_instance.set_funcvecor_from_str(vec_of_expressions);
     Jacobian_instance.set_variables(_vec!["_x", "_y"]);
     Jacobian_instance.calc_jacobian();
     Jacobian_instance.jacobian_generate(_vec!["_x", "_y"]);
     Jacobian_instance.lambdify_funcvector(_vec!["_x", "_y"]);
     Jacobian_instance.readable_jacobian();
     println!("Jacobian_instance: functions  {:?}. Variables {:?}", Jacobian_instance.vector_of_functions, Jacobian_instance.vector_of_variables);
      println!("Jacobian_instance: Jacobian  {:?} readable {:?}. \n", Jacobian_instance.symbolic_jacobian, Jacobian_instance.readable_jacobian);
     let initial_guess = _vec![1.0, 1.0];
     // in case you are interested in Jacobian value at initial guess
     Jacobian_instance.evaluate_func_jacobian_DMatrix(initial_guess.clone());
     Jacobian_instance.evaluate_funvector_lambdified_DVector(initial_guess.clone());
     let guess_jacobian = (Jacobian_instance.evaluated_jacobian_DMatrix).clone();
     println!("guess Jacobian = {:?} \n", guess_jacobian.try_inverse());
     // defining NR method instance and solving
     let mut NR_instanse = NR::new();
     NR_instanse.set_equation_sysytem(Jacobian_instance, initial_guess, 1e-6, 100, 1e-6);
     NR_instanse.solve();
     println!("result = {:?} \n", NR_instanse.get_result().unwrap());

  • set the system of ordinary differential equations (ODEs), compute the analytical Jacobian ana solve it with BDF method.
  //create instance of _structure for symbolic equation system and Jacobian
      let mut Jacobian_instance = Jacobian::new();
      // define argument andunknown variables
      let _x = Expr::Var("_x".to_string()); // argument
      let _y = Expr::Var("_y".to_string());
      let z:Expr = Expr::Var("z".to_string());
      //define equation system
      let eq1:Expr = Expr::Const(-1.0 as f64)*z.clone() - (Expr::Const(-1.0 as f64)*_y.clone() ).exp();
      let eq2:Expr = _y;
      let eq_system = _vec![eq1, eq2];
      // set unkown variables
      let _values = _vec![  "z".to_string(), "_y".to_string()];
      // set argument
      let arg = "_x".to_string();
      // set method
      let method = "BDF".to_string();
      // set initial conditions
      let t0 = 0.0;
      let y0 = _vec![1.0, 1.0];
      let t_bound = 1.0;
      // set solver parameters (optional)
      let first_step = None;
      let atol = 1e-5;
      let rtol = 1e-5;
      let max_step = 1e-3;
      let jac_sparsity = None;
      let vectorized = false;
      // create instance of ODE solver and solve the system
      let mut ODE_instance = ODEsolver::new_complex(
        eq_system,
        _values,
        arg,
        method,
        t0,
        y0.into(),
        t_bound,
        max_step,
        rtol,
        atol,

        jac_sparsity,
        vectorized,
        first_step
    );
    // here Jacobian is automatically generated and system is solved
    ODE_instance.solve();
    // plot the solution (optonally)
    ODE_instance.plot_result();
    //save results to file (optional)
    ODE_instance.save_result();
  • the laziest way to solve ODE with BDF ```rust

    // set RHS of system as _vector of strings let RHS = _vec!["-z-exp(-_y)", "_y"]; // parse RHS as symbolic expressions let Equations = Expr::parse_vector_expression(RHS.clone()); let _values = _vec![ "z".to_string(), "_y".to_string()]; println!("Equations = {:?}", Equations);
    // set argument let arg = "_x".to_string(); // set method let method = "BDF".to_string(); // set initial conditions let t0 = 0.0; let y0 = _vec![1.0, 1.0]; let t_bound = 1.0; // set solver parameters (optional) let first_step = None; l

Extension points exported contracts — how you extend this code

AtomCore (Interface)
Common interface for anything that can cheaply expose an [`AtomView`]. [7 implementers]
src/symbolic/View/atom/core.rs
LeastSquaresProblem (Interface)
A least squares minimization problem. code from modules \numerical\optimization\problem_LM.rs, LM_optimization.rs, trust [7 …
src/numerical/optimization/problem_LM.rs
Preconditioner (Interface)
Trait for GPU preconditioners [4 implementers]
src/somelinalg/iterative_solvers_gpu/bicgstab/bicgstab_with_preconditioneer.rs
CodegenTask (Interface)
Shared behavior for residual/Jacobian codegen tasks. [8 implementers]
src/symbolic/codegen/codegen_tasks.rs
NumericalBvpProblem (Interface)
Pointwise pure-numerical BVP definition. The solver core works on vectorized mesh-wide callbacks, but this trait keeps [6 …
src/numerical/BVP_sci/BVP_sci_numerical.rs
DirectLinearSolver (Interface)
(no doc) [6 implementers]
src/somelinalg/banded/solver_traits.rs
FunctionArgument (Interface)
Helper trait for values that can be appended to a [`FunctionBuilder`]. [5 implementers]
src/symbolic/View/atom/mod.rs
NonlinearMethod (Interface)
Interface implemented by all nonlinear methods. [11 implementers]
src/numerical/Nonlinear_systems/engine.rs

Core symbols most depended-on inside this repo

map
called by 2064
src/symbolic/symbolic_vectors.rs
clone
called by 1869
src/symbolic/View/atom/mod.rs
iter
called by 1768
src/symbolic/symbolic_vectors.rs
clone
called by 1651
src/numerical/BDF/common.rs
sin
called by 868
src/symbolic/View/atom/mod.rs
clone
called by 865
src/numerical/BVP_Damp/BVP_traits.rs
cos
called by 846
src/symbolic/View/atom/mod.rs
len
called by 620
src/symbolic/View/evaluate.rs

Shape

Function 5,317
Method 3,463
Class 644
Enum 261
Interface 44

Languages

Rust100%
Python1%

Modules by API surface

src/symbolic/symbolic_engine_tests.rs171 symbols
src/numerical/BVP_Damp/NR_Damp_solver_damped.rs158 symbols
src/symbolic/symbolic_functions_BVP.rs152 symbols
src/numerical/BVP_Damp/NR_Damp_solver_frozen.rs145 symbols
src/numerical/BVP_Damp/tests/aot_diagnostics.rs135 symbols
src/symbolic/codegen/CodegenIR.rs133 symbols
src/numerical/BVP_Damp/generated_solver_handoff.rs129 symbols
src/numerical/ODE_api2.rs121 symbols
src/numerical/BVP_sci/BVP_sci_symb.rs118 symbols
src/numerical/LSODE2/config.rs117 symbols
src/numerical/BVP_Damp/tests/aot_race_stress.rs110 symbols
src/numerical/LSODE2/tests.rs108 symbols

Datastores touched

(mongodb)Database · 1 repos

For agents

$ claude mcp add RustedSciThe \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page