Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Nonanti/mathcore/llms.txt

Use this file to discover all available pages before exploring further.

MathCore’s Optimization struct collects the tools you need for machine learning, numerical analysis, and scientific computing: symbolic gradients, full Hessian matrices, automatic differentiation at a point, iterative gradient descent, Jacobian evaluation, and Taylor series expansion. Every computation starts from a symbolic Expr, so results remain exact until you choose to evaluate them numerically.

Import

use mathcore::ml::Optimization;
use mathcore::MathCore;
use std::collections::HashMap;

Gradient

Optimization::gradient(expr: &Expr, vars: &[String]) -> Result<Vec<Expr>, MathError>

Computes the vector of partial derivatives ∂f/∂xᵢ for each variable listed in vars. The result is a Vec<Expr> — one symbolic expression per variable — that you can evaluate, simplify, or differentiate again.
use mathcore::ml::Optimization;
use mathcore::MathCore;

let loss = MathCore::parse("x^2 + y^2").unwrap();
let vars = vec!["x".to_string(), "y".to_string()];

let gradient = Optimization::gradient(&loss, &vars).unwrap();
println!("∂f/∂x = {}", gradient[0]); // 2*x
println!("∂f/∂y = {}", gradient[1]); // 2*y
Variable names not present in the expression produce a zero derivative. Variables not listed in vars are treated as constants.

Hessian

Optimization::hessian(expr: &Expr, vars: &[String]) -> Result<Vec<Vec<Expr>>, MathError>

Returns the n × n matrix of second-order partial derivatives H[i][j] = ∂²f / (∂xᵢ ∂xⱼ). The result is a Vec<Vec<Expr>> — a symbolic matrix you can inspect, evaluate, or pass to downstream analysis.
let loss = MathCore::parse("x^2 + 3*x*y + y^2").unwrap();
let vars = vec!["x".to_string(), "y".to_string()];

let hessian = Optimization::hessian(&loss, &vars).unwrap();
// hessian[0][0] = ∂²f/∂x²  = 2
// hessian[0][1] = ∂²f/∂x∂y = 3
// hessian[1][0] = ∂²f/∂y∂x = 3
// hessian[1][1] = ∂²f/∂y²  = 2
for row in &hessian {
    println!("{:?}", row.iter().map(|e| e.to_string()).collect::<Vec<_>>());
}
For convex functions the Hessian is positive semi-definite everywhere. Checking the sign of eigenvalues of the evaluated Hessian is a reliable way to verify a local minimum.

Gradient Descent

Optimization::gradient_descent(loss_fn: &Expr, initial_params: HashMap<String, f64>, learning_rate: f64, iterations: usize) -> Result<HashMap<String, f64>, MathError>

Iteratively minimises loss_fn from initial_params using vanilla gradient descent. At each step every parameter is updated by param -= learning_rate * ∂loss/∂param. Returns the final parameter map after iterations steps.
use mathcore::ml::Optimization;
use mathcore::MathCore;
use std::collections::HashMap;

let loss = MathCore::parse("x^2 + y^2").unwrap();

let mut params = HashMap::new();
params.insert("x".to_string(), 10.0);
params.insert("y".to_string(), 10.0);

let optimized = Optimization::gradient_descent(
    &loss,
    params,
    0.1,   // learning rate
    100,   // iterations
).unwrap();

println!("x ≈ {:.4}", optimized["x"]); // near 0
println!("y ≈ {:.4}", optimized["y"]); // near 0
The gradients are computed once before the loop using the symbolic expression, then evaluated numerically at each step. This means the gradient structure is fixed to the original expression — it does not adapt to a changing computational graph. For dynamic graphs use automatic_differentiation in a custom loop.
Minimising a shifted paraboloid — real-world example from examples/optimization.rs:
use mathcore::ml::Optimization;
use mathcore::parser::Parser;
use std::collections::HashMap;

// Minimum is at (3, 2)
let loss_fn = Parser::parse("(x-3)^2 + (y-2)^2").unwrap();

let mut initial_params = HashMap::new();
initial_params.insert("x".to_string(), 0.0);
initial_params.insert("y".to_string(), 0.0);

let optimized = Optimization::gradient_descent(
    &loss_fn,
    initial_params,
    0.1,
    100,
).unwrap();

println!("x = {:.4}, y = {:.4}", optimized["x"], optimized["y"]);
// x ≈ 3.0000, y ≈ 2.0000

Automatic Differentiation

Optimization::automatic_differentiation(expr: &Expr, var: &str, value: f64) -> Result<(f64, f64), MathError>

Evaluates both f(value) and f′(value) at the same point in a single call. Internally it performs symbolic differentiation with respect to var and then numerically evaluates both the original expression and the derivative. Returns (f(x), f'(x)) as a tuple of f64 values.
use mathcore::ml::Optimization;
use mathcore::MathCore;

let expr = MathCore::parse("x^3 - 2*x").unwrap();

let (f_val, f_prime) = Optimization::automatic_differentiation(&expr, "x", 2.0).unwrap();
println!("f(2)  = {}", f_val);   // 8 - 4 = 4
println!("f'(2) = {}", f_prime); // 3*4 - 2 = 10
automatic_differentiation is ideal when you need both the function value and its gradient at the same point — for example, when implementing custom line searches or Newton steps in a larger optimisation routine.

Taylor Series

Optimization::taylor_series(expr: &Expr, var: &str, center: f64, order: usize) -> Result<Expr, MathError>

Expands expr as a Taylor polynomial of degree order around var = center:
T(x) = Σ_{n=0}^{order}  f⁽ⁿ⁾(center) / n!  ·  (x − center)ⁿ
The result is a symbolic Expr you can evaluate, plot, or compare against the original function.
use mathcore::ml::Optimization;
use mathcore::MathCore;

// Taylor series of exp(x) around x=0, order 5
let func = MathCore::parse("exp(x)").unwrap();
let taylor = Optimization::taylor_series(&func, "x", 0.0, 5).unwrap();
println!("Taylor series: {}", taylor);
// 1 + x + 0.5*x^2 + 0.1667*x^3 + 0.0417*x^4 + 0.0083*x^5
Expanding multiple functions (from examples/optimization.rs):
use mathcore::ml::Optimization;
use mathcore::parser::Parser;

let functions = vec![
    ("sin(x)", 0.0, 7),
    ("exp(x)", 0.0, 5),
    ("ln(1+x)", 0.0, 5),
    ("1/(1-x)", 0.0, 5),
];

for (func_str, center, order) in functions {
    let func = Parser::parse(func_str).unwrap();
    let taylor = Optimization::taylor_series(&func, "x", center, order).unwrap();
    println!("Taylor series of {} around x={}: {}", func_str, center, taylor);
}
Terms whose coefficients evaluate to zero (after rounding) are omitted from the output expression. The resulting polynomial is already in a simplified form.

Jacobian

Optimization::jacobian(functions: &[Expr], vars: &[String], point: &HashMap<String, f64>) -> Result<DMatrix<f64>, MathError>

Computes the m × n Jacobian matrix at a specific numeric point, where m is the number of functions and n the number of variables. J[i][j] = ∂fᵢ/∂xⱼ evaluated at point. Returns a nalgebra DMatrix<f64>.
use mathcore::ml::Optimization;
use mathcore::MathCore;
use std::collections::HashMap;

// Functions: f1 = x^2 + y,  f2 = x*y
let f1 = MathCore::parse("x^2 + y").unwrap();
let f2 = MathCore::parse("x*y").unwrap();

let vars = vec!["x".to_string(), "y".to_string()];
let mut point = HashMap::new();
point.insert("x".to_string(), 1.0);
point.insert("y".to_string(), 2.0);

let jacobian = Optimization::jacobian(&[f1, f2], &vars, &point).unwrap();
// [ [2x, 1], [y, x] ] evaluated at (1, 2) = [[2, 1], [2, 1]]
println!("{}", jacobian);

Newton’s Method for Optimization

Optimization::optimize_newton(func: &Expr, var: &str, initial: f64, tolerance: f64, max_iterations: usize) -> Result<f64, MathError>

Finds a critical point of func (where f′(x) = 0) using Newton’s method on the derivative: x_ = x_n − f′(x_n) / f″(x_n). Converges quadratically near a non-degenerate extremum.
use mathcore::ml::Optimization;
use mathcore::parser::Parser;

// Find critical points of x^4 - 2*x^2 + x
let func = Parser::parse("x^4 - 2*x^2 + x").unwrap();

let result = Optimization::optimize_newton(
    &func,
    "x",
    -1.5,   // initial guess
    1e-8,   // tolerance
    50,     // max iterations
).unwrap();

println!("Critical point near -1.5: x ≈ {:.6}", result);
Newton’s method can diverge if the second derivative is close to zero or the initial point is far from any critical point. Supply multiple starting points (as in the example above) to explore different local minima.

Lagrange Multipliers

Optimization::lagrange_multipliers(objective: &Expr, constraints: &[Expr], vars: &[String]) -> Result<Vec<Expr>, MathError>

Builds the Lagrangian L = f + Σ λᵢ·gᵢ and returns its gradient with respect to all original variables and the introduced multipliers λᵢ. Setting each component to zero gives the KKT stationarity conditions.
use mathcore::ml::Optimization;
use mathcore::parser::Parser;

// Maximise f(x,y) = x*y subject to x^2 + y^2 - 1 = 0
let objective  = Parser::parse("x*y").unwrap();
let constraint = Parser::parse("x^2 + y^2 - 1").unwrap();

let gradient_eqs = Optimization::lagrange_multipliers(
    &objective,
    &[constraint],
    &["x".to_string(), "y".to_string()],
).unwrap();

for (i, eq) in gradient_eqs.iter().enumerate() {
    println!("∂L/∂var_{} = {}", i, eq);
}
// Analytical solution: x = ±1/√2, y = ±1/√2

Combined Workflow Example

use mathcore::ml::Optimization;
use mathcore::MathCore;
use std::collections::HashMap;

fn main() {
    // 1. Compute gradient of x^2 + y^2
    let loss = MathCore::parse("x^2 + y^2").unwrap();
    let vars = vec!["x".to_string(), "y".to_string()];
    let gradient = Optimization::gradient(&loss, &vars).unwrap();
    println!("∇f = [{}, {}]", gradient[0], gradient[1]);

    // 2. Taylor expansion of exp(x)
    let func = MathCore::parse("exp(x)").unwrap();
    let taylor = Optimization::taylor_series(&func, "x", 0.0, 5).unwrap();
    println!("Taylor series: {}", taylor);

    // 3. Run gradient descent to minimise loss
    let mut params = HashMap::new();
    params.insert("x".to_string(), 10.0);
    params.insert("y".to_string(), 10.0);

    let optimized = Optimization::gradient_descent(
        &loss, params, 0.1, 100,
    ).unwrap();
    println!("Optimized: {:?}", optimized);
}

Build docs developers (and LLMs) love