MINIMIZE
Overview
The MINIMIZE function finds a local minimum of a multivariate objective by wrapping scipy.optimize.minimize. Given a function , the solver iteratively explores the search space using gradient-based or gradient-free algorithms while respecting optional bound constraints. This enables optimization of engineering, finance, or operations models directly in Excel without building bespoke macros. This example function is provided as-is without any representation of accuracy.
Usage
To evaluate the function in Excel:
=MINIMIZE(func_expr, x_zero, [bounds], [minimize_method])func_expr(string, required): Objective expression written in terms ofx(e.g.,"(1 - x[0])**2 + x[1]**2").x_zero(2D list of float, required): Initial guess for each decision variable.bounds(2D list of float or None, optional): List of[lower, upper]bounds for each variable. UseNonefor an unbounded side.minimize_method(string (enum), optional): Name of a solver supported byscipy.optimize.minimize. Valid options:L-BFGS-B,BFGS,CG,Powell,Nelder-Mead,TNC,SLSQP,Dogleg,Trust-NCG.
The function returns a single-row 2D list containing the optimal variables followed by the objective value, or an error message string if validation fails or the optimization does not converge.
Function Expressions
The func_expr parameter accepts mathematical expressions using any of the following functions and constants. All functions are case-sensitive.
| Function/Constant | Description | Example |
|---|---|---|
sin(x) | Sine function (radians) | sin(x[0]) |
cos(x) | Cosine function (radians) | cos(x[1]) |
tan(x) | Tangent function (radians) | tan(x[0]) |
asin(x) or arcsin(x) | Arc sine function (radians) | asin(x[0]) |
acos(x) or arccos(x) | Arc cosine function (radians) | acos(x[1]) |
atan(x) or arctan(x) | Arc tangent function (radians) | atan(x[0]) |
sinh(x) | Hyperbolic sine | sinh(x[0]) |
cosh(x) | Hyperbolic cosine | cosh(x[1]) |
tanh(x) | Hyperbolic tangent | tanh(x[0]) |
exp(x) | Exponential function () | exp(x[0]) |
log(x) or ln(x) | Natural logarithm (base ) | log(x[0]) |
log10(x) | Base-10 logarithm | log10(x[0]) |
sqrt(x) | Square root | sqrt(x[0]) |
abs(x) | Absolute value | abs(x[0] - x[1]) |
pow(x, y) | Power function (x raised to power y) | pow(x[0], 2) |
pi | Mathematical constant π (≈3.14159) | x[0] * pi |
e | Mathematical constant e (≈2.71828) | e**x[0] |
Important Notes:
- All trigonometric functions use radians, not degrees
- Use
**(double asterisk) or^(caret) for exponentiation. Both work equivalently. - For multi-variable functions, use indexed variable notation:
x[0],x[1], etc.
Expression Examples
Common mathematical expressions and their func_expr notation:
- →
"(1-x[0])**2 + 100*(x[1]-x[0]**2)**2" - →
"x[0]**2 + x[1]**2"or"x[0]^2 + x[1]^2" - →
"(x[0]-1)**2 + (x[1]-2)**2" - →
"sin(x[0]) + cos(x[1])"
Examples
Example 1: Rosenbrock Function with All Parameters
Inputs:
| func_expr | x_zero | bounds | method | ||
|---|---|---|---|---|---|
| (1 - x[0])*2 + 100(x[1] - x[0]**2)**2 | -1 | 2 | -2 | 2 | L-BFGS-B |
| -2 | 2 |
Excel formula:
=MINIMIZE("(1 - x[0])**2 + 100*(x[1] - x[0]**2)**2", {-1,2}, {-2,2;-2,2}, "L-BFGS-B")Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 1.000 | 1.000 | 0.000 |
This example demonstrates using non-default values for all optional parameters.
Example 2: Quadratic Bowl with Bounds
Inputs:
| func_expr | x_zero | bounds | ||
|---|---|---|---|---|
| (x[0]-3)**2 + (x[1]-4)**2 + 7 | 1 | 1 | 0 | 0 |
| 10 | 10 |
Excel formula:
=MINIMIZE("(x[0]-3)**2 + (x[1]-4)**2 + 7", {1,1}, {0,10;0,10})Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 3.000 | 4.000 | 7.000 |
Example 3: Rosenbrock with BFGS Method
Inputs:
| func_expr | x_zero | method | |
|---|---|---|---|
| (1 - x[0])*2 + 100(x[1] - x[0]**2)**2 | -1 | 2 | BFGS |
Excel formula:
=MINIMIZE("(1 - x[0])**2 + 100*(x[1] - x[0]**2)**2", {-1,2}, , "BFGS")Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 1.000 | 1.000 | 0.000 |
Example 4: Quadratic with L-BFGS-B and Bounds
Inputs:
| func_expr | x_zero | bounds | method | ||
|---|---|---|---|---|---|
| (x[0]-3)**2 + (x[1]-4)**2 + 7 | 1 | 1 | 0 | 0 | L-BFGS-B |
| 10 | 10 |
Excel formula:
=MINIMIZE("(x[0]-3)**2 + (x[1]-4)**2 + 7", {1,1}, {0,10;0,10}, "L-BFGS-B")Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 3.000 | 4.000 | 7.000 |
Python Code
import math
from typing import List, Optional, Tuple, Union
import numpy as np
from scipy.optimize import minimize as scipy_minimize
Number = Union[int, float]
def minimize(
func_expr: str,
x_zero: List[List[Number]],
bounds: Optional[List[List[Optional[Number]]]] = None,
minimize_method: Optional[str] = None,
) -> Union[List[List[float]], str]:
"""Minimize a multivariate function using SciPy's ``minimize`` routine.
Args:
func_expr: Objective expression written in terms of ``x`` (for example, ``"x[0]**2 + x[1]**2"``).
x_zero: 2D list providing the initial guess for each variable.
bounds: Optional 2D list of ``[lower, upper]`` bounds per variable. Use ``None`` for an
unbounded side.
minimize_method: Optional string naming any solver supported by ``scipy.optimize.minimize``.
Returns:
Single-row 2D list containing the optimal variable values followed by the objective
value, or an error message string if validation fails or the solver cannot complete.
This example function is provided as-is without any representation of accuracy.
"""
def normalize_to_2d_list(value):
if not isinstance(value, list):
return [[value]]
return value
if not isinstance(func_expr, str) or func_expr.strip() == "":
return "Invalid input: func_expr must be a non-empty string."
if "x" not in func_expr:
return "Invalid input: function expression must contain the variable 'x'."
# Normalize x_zero in case Excel passes a scalar instead of a 2D list
x_zero = normalize_to_2d_list(x_zero)
if not isinstance(x_zero, list) or len(x_zero) == 0 or not isinstance(x_zero[0], list):
return "Invalid input: x_zero must be a 2D list, e.g., [[0, 0]]."
x0_row = x_zero[0]
if len(x0_row) == 0:
return "Invalid input: x_zero must contain at least one variable."
try:
x0_vector = np.asarray([float(value) for value in x0_row], dtype=float)
except (TypeError, ValueError):
return "Invalid input: x_zero must contain numeric values."
variable_count = x0_vector.size
processed_bounds: Optional[List[Tuple[Optional[float], Optional[float]]]] = None
if bounds is not None:
# Normalize bounds in case Excel passes a scalar instead of a 2D list
bounds = normalize_to_2d_list(bounds)
if not isinstance(bounds, list) or len(bounds) != variable_count:
return "Invalid input: bounds must provide one [min, max] pair per variable."
processed_bounds = []
for index, pair in enumerate(bounds):
if not isinstance(pair, list) or len(pair) != 2:
return "Invalid input: each bounds entry must be a [min, max] pair."
lower_raw, upper_raw = pair
try:
lower_val = float(lower_raw) if lower_raw is not None else None
upper_val = float(upper_raw) if upper_raw is not None else None
except (TypeError, ValueError):
return f"Invalid input: bounds for variable {index + 1} must be numeric or None."
if lower_val is not None and not math.isfinite(lower_val):
return f"Invalid input: bounds lower value for variable {index + 1} must be finite or None."
if upper_val is not None and not math.isfinite(upper_val):
return f"Invalid input: bounds upper value for variable {index + 1} must be finite or None."
if lower_val is not None and upper_val is not None and lower_val > upper_val:
return f"Invalid input: lower bound cannot exceed upper bound for variable {index + 1}."
processed_bounds.append((lower_val, upper_val))
solver_method = None
if minimize_method is not None:
# Check if method is a scalar that needs to be handled as a 2D list
if not isinstance(minimize_method, list):
# If method is not a list, treat it as a scalar and handle appropriately
if isinstance(minimize_method, str):
solver_method = minimize_method
else:
# If it's not a string and not a list, normalize it as a 2D list
minimize_method = [[minimize_method]]
if isinstance(minimize_method, list):
if len(minimize_method) == 0:
solver_method = None
elif isinstance(minimize_method[0], list) and len(minimize_method[0]) > 0:
solver_method = str(minimize_method[0][0])
elif isinstance(minimize_method[0], str):
solver_method = minimize_method[0]
else:
return "Invalid input: minimize_method must be a string, 2D list, or None."
elif isinstance(minimize_method, str):
solver_method = minimize_method
else:
return "Invalid input: minimize_method must be a string, 2D list, or None."
safe_globals = {
name: getattr(math, name)
for name in dir(math)
if not name.startswith("_")
}
safe_globals.update({
"np": np,
"numpy": np,
"sin": np.sin,
"cos": np.cos,
"tan": np.tan,
"asin": np.arcsin,
"acos": np.arccos,
"atan": np.arctan,
"sinh": np.sinh,
"cosh": np.cosh,
"tanh": np.tanh,
"exp": np.exp,
"log": np.log,
"sqrt": np.sqrt,
"abs": np.abs,
"pow": np.power,
})
# Objective wrapper that evaluates the expression for SciPy.
def _objective(vector: np.ndarray) -> float:
local_context = {"x": vector}
try:
value = eval(func_expr, safe_globals, local_context)
except Exception:
return float("inf")
try:
numeric_value = float(value)
except (TypeError, ValueError):
return float("inf")
if not math.isfinite(numeric_value):
return float("inf")
return numeric_value
# Pre-evaluate the objective at the initial guess to catch parse/eval errors early.
try:
initial_check = eval(func_expr, safe_globals, {"x": x0_vector})
except Exception as exc:
return f"Error: Invalid model expression at initial guess: {exc}"
try:
initial_value = float(initial_check)
except (TypeError, ValueError):
return "Error: Invalid model expression: objective did not return a numeric value at initial guess."
if not math.isfinite(initial_value):
return "Error: Invalid model expression: objective returned a non-finite value at initial guess."
minimize_kwargs = {}
if processed_bounds is not None:
minimize_kwargs["bounds"] = processed_bounds
if solver_method is not None:
minimize_kwargs["method"] = solver_method
try:
result = scipy_minimize(_objective, x0=x0_vector, **minimize_kwargs)
except ValueError as exc:
return f"minimize error: {exc}"
except Exception as exc:
return f"minimize error: {exc}"
if not result.success or result.x is None or result.fun is None:
message = result.message if hasattr(result, "message") else "Optimization failed."
return f"minimize failed: {message}"
if not math.isfinite(result.fun):
return "minimize failed: objective value is not finite."
try:
solution = [float(value) for value in result.x]
except (TypeError, ValueError):
return "minimize failed: solution vector could not be converted to floats."
return [solution + [float(result.fun)]]