Optimization and iterative solves

Minimization, root finding, nonlinear fitting and matrix-free linear solves.
import rustymath as rm

Minimization and scalar roots

rm.optimize takes SciPy’s names. minimize runs Brent’s method, Nelder–Mead, Powell’s method, L-BFGS or differential evolution. Without a method, it picks one from the number of variables, whether jac gives a gradient and whether there are bounds. root_scalar finds a zero of a function of one variable by Brent’s method, within a bracket or by stepping outward from a start. Here Nelder–Mead finds the minimum of Rosenbrock’s function, at (1, 1), and Brent’s method finds where cos x = x:

rosen = lambda x: (1-x[0])**2 + 100*(x[1]-x[0]**2)**2
rm.optimize.minimize(rosen, [-1.2, 1]).x, rm.optimize.root_scalar(lambda x: rm.cos(x) - x, [0, 1]).root
(Array([1.0, 1.0], dtype=float64), 0.7390851350713886)

Fitting and linear systems

least_squares fits by Levenberg–Marquardt. cg, minres, gmres and lsmr solve linear systems through the matrix’s products alone. They take the matrix as an array or as anything with a shape and a matvec method, such as a SciPy LinearOperator. An array’s products run in Rust, with no Python call for each product. Here least_squares recovers a = 2 and b = 0.3 from five points on y = a e^(bx), and lsmr gives the least-squares line through five points that no line fits:

t = rm.array([0., 0.5, 1, 1.5, 2])
fit = rm.optimize.least_squares(lambda p: p[0]*rm.exp(p[1]*t) - 2*rm.exp(0.3*t), [1, 1])
line = rm.optimize.lsmr([[1., 0], [1, 1], [1, 2], [1, 3], [1, 4]], [1, 2.9, 5.2, 6.8, 9.1])
fit.x, line.x
(Array([2.0, 0.3], dtype=float64), Array([0.98, 2.01], dtype=float64))