mathematicskit.optimization#
Gradient descent and nonlinear conjugate gradient (hand-rolled, exposing
the per-iterate path); Newton’s method and BFGS via
scipy.optimize.minimize; Lagrange multipliers and KKT-condition
verification; linear programming via scipy.optimize.linprog; a
quadratic-penalty method for constrained problems; and convergence-rate
comparison utilities on shared test functions (Rosenbrock, a quadratic
bowl).
mathematicskit.optimization: unconstrained and constrained optimization.
Gradient descent (fixed and backtracking-line-search step sizes) and
nonlinear conjugate gradient (Fletcher-Reeves/Polak-Ribiere), hand-rolled
to expose the per-iterate path for convergence-rate comparisons; Newton’s
method and BFGS via scipy.optimize.minimize, with the iterate path
recorded via its callback; Lagrange-multiplier stationary points and KKT-
condition verification for constrained problems; linear programming via
scipy.optimize.linprog; a quadratic-penalty method for constrained
nonlinear problems; integer programming (branch and bound via
scipy.optimize.milp), zero-sum matrix games, Levenberg-Marquardt
least squares, Robbins-Monro stochastic approximation, golden-section
search, the Frank-Wolfe method, knapsack dynamic programming, Nelder-Mead
simplex search, Nesterov acceleration, and Adam; and convergence-rate comparison utilities across
methods on a shared set of test functions (Rosenbrock, a quadratic bowl).
- class mathematicskit.optimization.Adam(alpha=0.001, beta1=0.9, beta2=0.999, eps=1e-08, tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerAdam: adaptive moment estimation (Kingma and Ba, 2014).
Keeps exponential moving averages of the gradient and of its elementwise square, corrects their bias toward zero, and scales each coordinate’s step by the root-mean-square gradient:
\[m_k = \beta_1 m_{k-1} + (1-\beta_1) g_k, \qquad v_k = \beta_2 v_{k-1} + (1-\beta_2) g_k^2,\]\[x_{k+1} = x_k - \alpha \frac{m_k / (1-\beta_1^k)}{\sqrt{v_k/(1-\beta_2^k)} + \varepsilon}.\]Each coordinate moves by roughly
alphaper step regardless of the gradient’s scale, which makes Adam robust to badly scaled and noisy (stochastic) gradients. With a constantalphait settles within aboutalphaof a minimizer rather than converging exactly.- Parameters:
Examples
>>> import numpy as np >>> f = lambda x: (x[0] - 1.0) ** 2 + 1000.0 * (x[1] + 2.0) ** 2 >>> grad = lambda x: np.array([2.0 * (x[0] - 1.0), 2000.0 * (x[1] + 2.0)]) >>> result = Adam(alpha=0.05, max_iter=3000).minimize(f, grad, np.array([0.0, 0.0])) >>> np.allclose(result.x, [1.0, -2.0], atol=1e-2) True
- class mathematicskit.optimization.BFGS(tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerQuasi-Newton BFGS, via
scipy.optimize.minimize(method="BFGS").Builds up an approximation to the inverse Hessian purely from successive gradient evaluations (the BFGS update), converging superlinearly without ever needing an explicit Hessian – the standard practical alternative to Newton’s method when second derivatives are unavailable or expensive. See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 6.1, eq. (6.19).
- Parameters:
Examples
>>> import numpy as np >>> from mathematicskit.optimization.utils.test_functions import rosenbrock, rosenbrock_grad >>> result = BFGS().minimize(rosenbrock, rosenbrock_grad, np.array([-1.2, 1.0])) >>> np.allclose(result.x, [1.0, 1.0], atol=1e-3) True
- class mathematicskit.optimization.GameResult(row_strategy, col_strategy, value)[source]#
Bases:
objectContainer for the solution of a two-player zero-sum matrix game.
- class mathematicskit.optimization.GradientDescent(alpha=0.1, tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerFixed-step gradient descent: \(x_{k+1} = x_k - \alpha \nabla f(x_k)\).
The simplest descent method: always steps a constant multiple
alphaof the negative gradient. Converges linearly foralphasmall enough (alpha < 2/LforL-smooth convexf), but a poor choice ofalphaeither diverges (too large) or converges glacially (too small) – unlikeGradientDescentLineSearch, which adapts the step automatically. See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 2.2.- Parameters:
Examples
>>> import numpy as np >>> f = lambda x: x[0] ** 2 + x[1] ** 2 >>> grad = lambda x: np.array([2.0 * x[0], 2.0 * x[1]]) >>> result = GradientDescent(alpha=0.1, tol=1e-10).minimize(f, grad, np.array([3.0, 4.0])) >>> np.allclose(result.x, [0.0, 0.0], atol=1e-6) True
- class mathematicskit.optimization.GradientDescentLineSearch(c1=0.0001, rho=0.5, alpha0=1.0, tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerGradient descent with backtracking (Armijo) line search.
\(x_{k+1} = x_k + \alpha_k d_k\), \(d_k = -\nabla f(x_k)\), with \(\alpha_k\) chosen each step by
backtracking_line_search()rather than fixed in advance – robust to poor scaling that would make a fixed step size either diverge or crawl. See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 3.1.- Parameters:
c1 (
float) – Armijo sufficient-decrease constant and backtrack shrink factor (forwarded tobacktracking_line_search()).rho (
float) – Armijo sufficient-decrease constant and backtrack shrink factor (forwarded tobacktracking_line_search()).alpha0 (
float) – Initial step length tried at each iteration.tol (
float) – Convergence tolerance on||grad(x)||.max_iter (
int)
Examples
>>> import numpy as np >>> f = lambda x: x[0] ** 2 + 10.0 * x[1] ** 2 >>> grad = lambda x: np.array([2.0 * x[0], 20.0 * x[1]]) >>> result = GradientDescentLineSearch(tol=1e-10).minimize(f, grad, np.array([3.0, 4.0])) >>> np.allclose(result.x, [0.0, 0.0], atol=1e-6) True
- class mathematicskit.optimization.KKTResult(x, stationarity_residual, primal_feasible, dual_feasible, complementary_slackness, satisfied=False)[source]#
Bases:
objectContainer for a numerical Karush-Kuhn-Tucker condition check at a candidate point.
- Parameters:
- complementary_slackness: bool#
Whether \(\mu_j g_j(x) \approx 0\) for every inequality constraint.
- Type:
- primal_feasible: bool#
Whether every equality constraint is (numerically) zero and every inequality constraint is \(\leq 0\) at
x.- Type:
- class mathematicskit.optimization.KnapsackResult(value, items, weight, table=<factory>)[source]#
Bases:
objectContainer for the solution of a 0/1 knapsack problem.
- class mathematicskit.optimization.LagrangeResult(x, multipliers, converged=True, kkt=None)[source]#
Bases:
objectContainer for an equality-constrained stationary point found via the Lagrange-multiplier system.
- class mathematicskit.optimization.LeastSquaresResult(x, cost, residuals, success=True, nfev=0, message='')[source]#
Bases:
objectContainer for the output of a nonlinear least-squares fit.
- class mathematicskit.optimization.LinearProgramResult(x, fun, success, message='')[source]#
Bases:
objectContainer for the output of
linear_program().
- class mathematicskit.optimization.NelderMead(tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerDerivative-free Nelder-Mead simplex search.
- Parameters:
Examples
>>> import numpy as np >>> from mathematicskit.optimization.utils.test_functions import rosenbrock >>> result = NelderMead(tol=1e-10).minimize(rosenbrock, None, np.array([-1.2, 1.0])) >>> np.allclose(result.x, [1.0, 1.0], atol=1e-6) True
- class mathematicskit.optimization.NesterovAcceleratedGradient(alpha=0.1, tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerNesterov’s accelerated gradient method (1983).
Takes the gradient step from an extrapolated point:
\[y_k = x_k + \frac{k-1}{k+2}(x_k - x_{k-1}), \qquad x_{k+1} = y_k - \alpha \nabla f(y_k).\]For convex
fwithL-Lipschitz gradient and \(\alpha = 1/L\), \(f(x_k) - f^* = O(1/k^2)\), against \(O(1/k)\) for plain gradient descent – the optimal rate for any first-order method.- Parameters:
Examples
>>> import numpy as np >>> f = lambda x: 0.5 * (x[0] ** 2 + 100.0 * x[1] ** 2) >>> grad = lambda x: np.array([x[0], 100.0 * x[1]]) >>> result = NesterovAcceleratedGradient(alpha=0.01, tol=1e-8, max_iter=5000).minimize(f, grad, np.array([1.0, 1.0])) >>> np.allclose(result.x, [0.0, 0.0], atol=1e-6) True
- class mathematicskit.optimization.NewtonMethod(tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerNewton’s method via
scipy.optimize.minimize(method="Newton-CG").Each step solves \(\nabla^2 f(x_k) p_k = -\nabla f(x_k)\) (approximately, via a truncated conjugate-gradient sub-solve, which is what makes “Newton-CG” practical for large problems without forming/factoring the Hessian explicitly) and steps along a line-searched multiple of \(p_k\). Converges quadratically near a minimizer where the Hessian is positive-definite – far faster than
gradient_descenton ill-conditioned problems likerosenbrock(). See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 6 and Ch. 7.1 (Newton-CG).- Parameters:
Examples
>>> import numpy as np >>> from mathematicskit.optimization.utils.test_functions import rosenbrock, rosenbrock_grad, rosenbrock_hess >>> result = NewtonMethod().minimize(rosenbrock, rosenbrock_grad, np.array([-1.2, 1.0]), hess=rosenbrock_hess) >>> np.allclose(result.x, [1.0, 1.0], atol=1e-4) True
- class mathematicskit.optimization.NonlinearConjugateGradient(variant='polak_ribiere', c1=0.0001, rho=0.5, alpha0=1.0, tol=1e-08, max_iter=1000)[source]#
Bases:
UnconstrainedOptimizerNonlinear conjugate gradient with a backtracking line search.
Generates search directions \(d_{k+1} = -\nabla f(x_{k+1}) + \beta_k d_k\), restarting at \(d_0 = -\nabla f(x_0)\). Two classic choices of \(\beta_k\):
Fletcher-Reeves: \(\beta_k^{FR} = \dfrac{\nabla f(x_{k+1})^T \nabla f(x_{k+1})}{\nabla f(x_k)^T \nabla f(x_k)}\).
Polak-Ribiere (the default, with the standard \(\max(0, \cdot)\) safeguard that automatically restarts along the steepest-descent direction if \(\beta_k\) would go negative): \(\beta_k^{PR+} = \max\!\left(0, \dfrac{\nabla f(x_{k+1})^T(\nabla f(x_{k+1}) - \nabla f(x_k))}{\nabla f(x_k)^T \nabla f(x_k)}\right)\).
See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 5.2, eq. (5.41)-(5.45).
- Parameters:
Examples
>>> import numpy as np >>> f = lambda x: x[0] ** 2 + 10.0 * x[1] ** 2 >>> grad = lambda x: np.array([2.0 * x[0], 20.0 * x[1]]) >>> result = NonlinearConjugateGradient(tol=1e-10).minimize(f, grad, np.array([3.0, 4.0])) >>> np.allclose(result.x, [0.0, 0.0], atol=1e-6) True
- class mathematicskit.optimization.OptimizeResult(x, fun, path, iterations=0, converged=True, method='', extra=<factory>)[source]#
Bases:
objectContainer for the output of an
UnconstrainedOptimizer.- Parameters:
- extra: dict#
Free-form slot for method-specific diagnostics (e.g. the step-size history for a line-search method, or the penalty-parameter schedule for
PenaltyMethod).- Type:
- class mathematicskit.optimization.PenaltyMethod(mu0=1.0, mu_factor=10.0, n_outer=10, inner_optimizer=None)[source]#
Bases:
objectQuadratic penalty method: a sequence of unconstrained minimizations with an increasing penalty weight.
Reformulates \(\min f(x)\) s.t. \(h(x)=0\), \(g(x)\leq0\) as the unconstrained problem \(\min_x f(x) + \mu \left[\sum_i h_i(x)^2 + \sum_j \max(0, g_j(x))^2\right]\), solved for a sequence of increasing \(\mu\), each warm-started from the previous solution – as \(\mu \to \infty\) the penalized minimizer converges to a KKT point of the original constrained problem. Each unconstrained sub-solve uses
BFGSby default. See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 17.1 (“The Quadratic Penalty Method”).- Parameters:
mu0 (
float) – Initial penalty weight.mu_factor (
float) – Multiplicative increase applied tomuafter each sub-solve.n_outer (
int) – Number of penalty-weight increases (outer iterations).inner_optimizer (
UnconstrainedOptimizer|None) – Solver used for each unconstrained sub-problem; defaults toBFGS.
Examples
>>> import numpy as np >>> # minimize x^2 + y^2 subject to x + y = 1: exact solution (0.5, 0.5). >>> f = lambda z: z[0] ** 2 + z[1] ** 2 >>> grad_f = lambda z: np.array([2.0 * z[0], 2.0 * z[1]]) >>> h = lambda z: np.array([z[0] + z[1] - 1.0]) >>> grad_h = lambda z: np.array([[1.0, 1.0]]) >>> result = PenaltyMethod(n_outer=8).minimize(f, grad_f, np.array([0.0, 0.0]), h=h, grad_h=grad_h) >>> np.allclose(result.x, [0.5, 0.5], atol=1e-3) True
- minimize(f, grad_f, x0, h=None, grad_h=None, g=None, grad_g=None)[source]#
Run the sequential penalty minimization.
- Parameters:
f (
Callable[[ndarray],float]) – Objective and its gradient.grad_f (
Callable[[ndarray],ndarray]) – Objective and its gradient.x0 (
ndarray)h (
Callable[[ndarray],ndarray] |None) – Equality constraintsh(x) -> ndarrayand their Jacobiangrad_h(x) -> ndarrayshape (m, n).grad_h (
Callable[[ndarray],ndarray] |None) – Equality constraintsh(x) -> ndarrayand their Jacobiangrad_h(x) -> ndarrayshape (m, n).g (
Callable[[ndarray],ndarray] |None) – Inequality constraintsg(x) <= 0and their Jacobian.grad_g (
Callable[[ndarray],ndarray] |None) – Inequality constraintsg(x) <= 0and their Jacobian.
- Return type:
- Returns:
OptimizeResult –
extra["mu_history"]records the penalty weight used at each outer iteration.
- class mathematicskit.optimization.ScalarSearchResult(x, fun, brackets, iterations=0, nfev=0, converged=True)[source]#
Bases:
objectContainer for a one-dimensional bracketing minimization.
- class mathematicskit.optimization.UnconstrainedOptimizer(tol=1e-08, max_iter=1000)[source]#
Bases:
ABCCommon base for iterative unconstrained-minimization algorithms.
- Parameters:
- mathematicskit.optimization.compare_optimizers(optimizers, f, grad, x0)[source]#
Run several optimizers from the same starting point on the same problem.
- Parameters:
- Return type:
- Returns:
dict of str -> OptimizeResult
Examples
>>> import numpy as np >>> from mathematicskit.optimization.systems.gradient_descent import GradientDescent, GradientDescentLineSearch >>> f = lambda x: x[0] ** 2 + x[1] ** 2 >>> grad = lambda x: np.array([2.0 * x[0], 2.0 * x[1]]) >>> results = compare_optimizers({"fixed": GradientDescent(alpha=0.1), "line_search": GradientDescentLineSearch()}, f, grad, np.array([3.0, 4.0])) >>> sorted(results.keys()) ['fixed', 'line_search']
- mathematicskit.optimization.frank_wolfe(f, grad, x0, a_ub=None, b_ub=None, a_eq=None, b_eq=None, bounds=None, tol=1e-06, max_iter=1000)[source]#
Minimize a smooth convex
fover a bounded polytope by the Frank-Wolfe method.The feasible set \(P = \{x : A_{ub}x \leq b_{ub},\ A_{eq}x = b_{eq},\ \text{bounds}\}\) is described exactly as for
linear_program()(default bounds(0, None)) and must be bounded.- Parameters:
- Return type:
- Returns:
OptimizeResult –
extra["gaps"]holds the Frank-Wolfe gap at every iteration, an upper bound on \(f(x_k) - f^*\).
Examples
>>> import numpy as np >>> y = np.array([0.2, 0.3, 0.5]) # already on the probability simplex >>> f = lambda x: float(np.sum((x - y) ** 2)) >>> grad = lambda x: 2.0 * (x - y) >>> result = frank_wolfe(f, grad, [1.0, 0.0, 0.0], a_eq=np.ones((1, 3)), b_eq=np.array([1.0]), max_iter=2000) >>> np.allclose(result.x, y, atol=1e-2) True
- mathematicskit.optimization.function_value_gap(result, f, f_star)[source]#
Compute
f(x_k) - f*at every recorded iterate – the standard y-axis for a convergence-rate plot (log-scale reveals linear vs. superlinear vs. quadratic convergence as a straight, curving-down, or sharply-curving-down line, respectively).- Parameters:
result (
OptimizeResult) – From anyUnconstrainedOptimizer.f_star (
float) – The known (or best available) optimal value.
- Return type:
- Returns:
ndarray, shape (iterations + 1,)
Examples
>>> import numpy as np >>> from mathematicskit.optimization.systems.gradient_descent import GradientDescent >>> f = lambda x: x[0] ** 2 >>> grad = lambda x: np.array([2.0 * x[0]]) >>> result = GradientDescent(alpha=0.1, tol=1e-10).minimize(f, grad, np.array([1.0])) >>> gap = function_value_gap(result, f, f_star=0.0) >>> bool(np.all(np.diff(gap) <= 1e-12)) True
- mathematicskit.optimization.golden_section_search(f, a, b, tol=1e-08, max_iter=1000)[source]#
Minimize a unimodal
fon[a, b]by golden-section search.Two interior points \(c = b - (b-a)/\varphi\) and \(d = a + (b-a)/\varphi\) split the bracket in the golden ratio. Comparing \(f(c)\) with \(f(d)\) discards one end, and the surviving interior point is reused in the next bracket, so each iteration costs a single new evaluation and shrinks the bracket by the factor \(1/\varphi\).
- Parameters:
- Return type:
- Returns:
ScalarSearchResult
Examples
>>> result = golden_section_search(lambda x: (x - 2.0) ** 2, 0.0, 5.0) >>> round(result.x, 6) 2.0
- mathematicskit.optimization.integer_linear_program(c, a_ub=None, b_ub=None, a_eq=None, b_eq=None, bounds=None, integrality=None)[source]#
Solve a (mixed-)integer linear program by branch and bound.
Same problem as
linear_program()with some or all variables restricted to integers. Land and Doig’s branch-and-bound solves the LP relaxation, then splits on a fractional variable \(x_j = t\) into the subproblems \(x_j \leq \lfloor t \rfloor\) and \(x_j \geq \lceil t \rceil\), pruning any branch whose relaxation cannot beat the best integer solution found so far. This wrapsscipy.optimize.milp()(HiGHS branch-and-cut).- Parameters:
c (
ndarray) – As forlinear_program()(default bounds(0, None)).a_ub (
ndarray|None) – As forlinear_program()(default bounds(0, None)).b_ub (
ndarray|None) – As forlinear_program()(default bounds(0, None)).a_eq (
ndarray|None) – As forlinear_program()(default bounds(0, None)).b_eq (
ndarray|None) – As forlinear_program()(default bounds(0, None)).bounds – As for
linear_program()(default bounds(0, None)).integrality (array_like of int, shape (n,), optional) –
1for an integer variable,0for a continuous one; defaults to all integer.
- Return type:
- Returns:
LinearProgramResult
Examples
>>> import numpy as np >>> # maximize 5x + 4y s.t. 6x + 4y <= 24, x + 2y <= 6, x, y >= 0 integer. >>> result = integer_linear_program(np.array([-5.0, -4.0]), a_ub=np.array([[6.0, 4.0], [1.0, 2.0]]), b_ub=np.array([24.0, 6.0])) >>> (result.x.round() + 0.0).tolist(), round(-result.fun, 6) ([4.0, 0.0], 20.0)
- mathematicskit.optimization.knapsack(values, weights, capacity)[source]#
Solve the 0/1 knapsack problem by dynamic programming.
- Parameters:
- Return type:
- Returns:
KnapsackResult
Examples
>>> result = knapsack([60, 100, 120], [10, 20, 30], 50) >>> result.value, result.items.tolist(), result.weight (220.0, [1, 2], 50)
- mathematicskit.optimization.lagrange_stationary_point(grad_f, h, grad_h, x0, lambda0=None)[source]#
Solve the equality-constrained Lagrange stationarity system.
Finds
(x*, lambda*)satisfying \(\nabla f(x^*) + J_h(x^*)^T \lambda^* = 0\) and \(h(x^*) = 0\) simultaneously, viascipy.optimize.root()on the stacked system – the standard way to locate a constrained stationary point once you’ve written down the first-order (Lagrange) conditions symbolically. See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 12.2-12.3.- Parameters:
grad_f (
Callable[[ndarray],ndarray]) – Gradient of the objective,grad_f(x) -> ndarrayshape (n,).h (
Callable[[ndarray],ndarray]) – Equality constraints,h(x) -> ndarrayshape (m,).grad_h (
Callable[[ndarray],ndarray]) – Constraint Jacobian,grad_h(x) -> ndarrayshape (m, n).x0 (ndarray, shape (n,)) – Initial guess for
x.lambda0 (ndarray, shape (m,), optional) – Initial guess for the multipliers; defaults to zeros.
- Return type:
- Returns:
LagrangeResult
Examples
>>> import numpy as np >>> # minimize x^2 + y^2 subject to x + y = 1: exact solution (0.5, 0.5), lambda=-1. >>> grad_f = lambda z: np.array([2.0 * z[0], 2.0 * z[1]]) >>> h = lambda z: np.array([z[0] + z[1] - 1.0]) >>> grad_h = lambda z: np.array([[1.0, 1.0]]) >>> result = lagrange_stationary_point(grad_f, h, grad_h, x0=np.array([0.0, 0.0])) >>> np.allclose(result.x, [0.5, 0.5], atol=1e-8) True >>> np.allclose(result.multipliers, [-1.0], atol=1e-8) True
- mathematicskit.optimization.levenberg_marquardt(residual, x0, jac=None, tol=1e-10, max_nfev=None)[source]#
Minimize \(\tfrac12 \sum_i r_i(x)^2\) with the Levenberg-Marquardt method.
Thin wrapper around
scipy.optimize.least_squares(method="lm")(MINPACK’slmder/lmdif). Requires at least as many residuals as parameters.- Parameters:
residual (
Callable[[ndarray],ndarray]) –residual(x) -> ndarrayof shape(m,).x0 (array_like, shape (n,)) – Initial parameter guess.
jac (
Callable[[ndarray],ndarray] |None) –jac(x) -> ndarrayof shape(m, n); estimated by finite differences if omitted.tol (
float) – Forwarded asftol,xtolandgtol.max_nfev (
int|None) – Maximum number of residual evaluations.
- Return type:
- Returns:
LeastSquaresResult
Examples
>>> import numpy as np >>> t = np.linspace(0.0, 4.0, 20) >>> y = 2.0 * np.exp(-0.5 * t) >>> result = levenberg_marquardt(lambda p: p[0] * np.exp(-p[1] * t) - y, [1.0, 1.0]) >>> np.allclose(result.x, [2.0, 0.5]) True
- mathematicskit.optimization.linear_program(c, a_ub=None, b_ub=None, a_eq=None, b_eq=None, bounds=None)[source]#
Solve a linear program \(\min_x c^T x\) subject to \(A_{ub}x \leq b_{ub}\), \(A_{eq}x = b_{eq}\).
Thin wrapper around
scipy.optimize.linprog()(method="highs", scipy’s default modern solver, which dispatches between a dual simplex and an interior-point method depending on problem structure). See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 13.- Parameters:
c (
ndarray) – Objective coefficients (minimized).b_ub (
ndarray|None) – Inequality constraints \(A_{ub} x \leq b_{ub}\).b_eq (
ndarray|None) – Equality constraints \(A_{eq} x = b_{eq}\).bounds (sequence of (float, float), optional) – Per-variable bounds; defaults to
(0, None)for every variable (scipy’s convention, matching the standard-form LP).
- Return type:
- Returns:
LinearProgramResult
Examples
>>> import numpy as np >>> # minimize -x - 2y subject to x + y <= 4, x <= 3, x, y >= 0. >>> result = linear_program(c=np.array([-1.0, -2.0]), a_ub=np.array([[1.0, 1.0], [1.0, 0.0]]), b_ub=np.array([4.0, 3.0])) >>> result.success True >>> np.allclose(result.x, [0.0, 4.0], atol=1e-6) True >>> round(result.fun, 6) -8.0
- mathematicskit.optimization.quadratic_bowl(x, matrix=None, b=None)[source]#
A convex quadratic bowl \(f(x) = \tfrac12 x^T A x - b^T x\).
For symmetric positive-definite
A, the unique minimizer is \(x^* = A^{-1}b\), so this is the standard convex test problem (any descent method converges, but at rates governed by \(\kappa(A)\), the condition number).- Parameters:
- Return type:
- Returns:
float
Examples
>>> round(quadratic_bowl(np.array([0.0, 0.0])), 10) 0.0
- mathematicskit.optimization.quadratic_bowl_grad(x, matrix=None, b=None)[source]#
Gradient of
quadratic_bowl(): \(\nabla f(x) = Ax - b\).
- mathematicskit.optimization.quadratic_bowl_hess(x, matrix=None, b=None)[source]#
Hessian of
quadratic_bowl(): the constant matrixAitself.- Parameters:
x (
ndarray)b (
ndarray|None) – Unused; accepted for a uniform(x, matrix, b)signature alongsidequadratic_bowl()/quadratic_bowl_grad().
- Return type:
- Returns:
ndarray, shape (n, n)
- mathematicskit.optimization.robbins_monro(noisy_grad, x0, a0=1.0, n_iter=1000, seed=None, f=None)[source]#
Stochastic gradient descent with Robbins-Monro steps \(a_k = a_0/(k+1)\).
- Parameters:
noisy_grad (
Callable[[ndarray,Generator],ndarray]) –noisy_grad(x, rng) -> ndarray: an unbiased, noisy observation of \(\nabla f(x)\) (or, more generally, of the function whose root is sought), drawing its randomness fromrng.x0 (array_like) – Starting point.
a0 (
float) – Step-size scale; the step at iterationk(from 0) isa0 / (k + 1).n_iter (
int) – Number of iterations (there is no reliable stopping test on noisy data).seed (
int|None) – Seed for thenumpy.random.Generatorpassed tonoisy_grad.f (
Callable[[ndarray],float] |None) – The (noise-free) objective, used only to reportfun;nanif omitted.
- Return type:
- Returns:
OptimizeResult
Notes
For \(f(x) = \tfrac12\|x - \mu\|^2\) and
a0 = 1the iterate afternsteps is exactly the sample mean of thennoisy observations of \(\mu\) – the running average is the simplest stochastic-approximation scheme.Examples
>>> import numpy as np >>> noisy = lambda x, rng: x - 3.0 + rng.normal(size=x.shape) >>> result = robbins_monro(noisy, [0.0], n_iter=20000, seed=0) >>> bool(abs(result.x[0] - 3.0) < 0.05) True
- mathematicskit.optimization.rosenbrock(x, a=1.0, b=100.0)[source]#
The (generalized, n-dimensional) Rosenbrock “banana” function.
\(f(x) = \sum_{i=1}^{n-1} \left[b(x_{i+1}-x_i^2)^2 + (a-x_i)^2\right]\). A narrow, curved valley with a unique global minimum \(f(a, a, \dots, a) = 0\); gradient descent zig-zags slowly along the valley floor while Newton/quasi-Newton methods that exploit curvature converge far faster – the standard demonstration of why second-order information matters. See Nocedal & Wright, Numerical Optimization, 2nd ed., eq. 2.22.
- Parameters:
- Return type:
- Returns:
float
Examples
>>> rosenbrock(np.array([1.0, 1.0])) 0.0
- mathematicskit.optimization.rosenbrock_grad(x, a=1.0, b=100.0)[source]#
Gradient of
rosenbrock().Examples
>>> np.allclose(rosenbrock_grad(np.array([1.0, 1.0])), 0.0) True
- mathematicskit.optimization.rosenbrock_hess(x, a=1.0, b=100.0)[source]#
Hessian of
rosenbrock()(tridiagonal).Examples
>>> H = rosenbrock_hess(np.array([1.0, 1.0])) >>> H.shape (2, 2)
- mathematicskit.optimization.solve_zero_sum_game(payoff)[source]#
Optimal mixed strategies and value of a zero-sum matrix game.
payoff[i, j]is what the column player pays the row player when rowimeets columnj. The row player solves\[\max_{p, v} v \quad \text{s.t.} \quad A^T p \geq v \mathbf{1}, \quad \mathbf{1}^T p = 1, \quad p \geq 0,\]and the column player solves the mirror-image minimization; LP duality makes the two optimal values equal, which is von Neumann’s minimax theorem.
- Parameters:
payoff (array_like, shape (m, n)) – Payoff matrix to the row (maximizing) player.
- Return type:
- Returns:
GameResult
Examples
>>> import numpy as np >>> rps = np.array([[0, -1, 1], [1, 0, -1], [-1, 1, 0]]) # rock-paper-scissors >>> result = solve_zero_sum_game(rps) >>> np.allclose(result.row_strategy, 1 / 3), abs(round(result.value, 10)) (True, 0.0)
- mathematicskit.optimization.verify_kkt(x, grad_f, h=None, grad_h=None, eq_multipliers=None, g=None, grad_g=None, ineq_multipliers=None, tol=1e-06)[source]#
Numerically check the KKT conditions at a candidate point.
Verifies, for equality constraints \(h(x)=0\) with multipliers \(\lambda\) and inequality constraints \(g(x) \leq 0\) with multipliers \(\mu \geq 0\):
Stationarity: \(\nabla f(x) + \sum_i \lambda_i \nabla h_i(x) + \sum_j \mu_j \nabla g_j(x) \approx 0\).
Primal feasibility: \(h(x) \approx 0\), \(g(x) \leq \text{tol}\).
Dual feasibility: \(\mu \geq -\text{tol}\).
Complementary slackness: \(\mu_j g_j(x) \approx 0\) for every
j.
See Nocedal & Wright, Numerical Optimization, 2nd ed., Ch. 12.3, Theorem 12.1.
- Parameters:
x (
ndarray) – Candidate point.h (
Callable[[ndarray],ndarray] |None) – Equality constraints and their Jacobian.grad_h (
Callable[[ndarray],ndarray] |None) – Equality constraints and their Jacobian.g (
Callable[[ndarray],ndarray] |None) – Inequality constraints and their Jacobian.grad_g (
Callable[[ndarray],ndarray] |None) – Inequality constraints and their Jacobian.tol (
float) – Numerical tolerance for each check.
- Return type:
- Returns:
KKTResult
Examples
>>> import numpy as np >>> # minimize x^2 + y^2 subject to x + y = 1: (0.5, 0.5) with lambda=-1 is exactly KKT-stationary. >>> grad_f = lambda z: np.array([2.0 * z[0], 2.0 * z[1]]) >>> h = lambda z: np.array([z[0] + z[1] - 1.0]) >>> grad_h = lambda z: np.array([[1.0, 1.0]]) >>> result = verify_kkt(np.array([0.5, 0.5]), grad_f, h=h, grad_h=grad_h, eq_multipliers=np.array([-1.0])) >>> result.satisfied True