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);
}