BRUTE
Overview
The BRUTE function conducts a grid search over a Cartesian grid to approximate the global minimum of a multivariate function, wrapping scipy.optimize.brute. It evaluates the objective at evenly spaced points across the specified bounds and optionally refines the best grid point with a local optimizer. This example function is provided as-is without any representation of accuracy.
Usage
To perform a brute-force search in Excel:
=BRUTE(func_expr, bounds, [ns], [finish])func_expr(string, required): Objective expression usingx[i]to reference each variable.bounds(2D list, required): Each row is[min, max]defining the search interval for a variable.ns(int, optional, default=20): Number of equally spaced grid points per dimension (must be at least 2).finish(string (enum) or blank, optional, default="fmin"): Local refinement applied to the best grid point. Valid options:fmin,fmin_powell, or leave blank to skip refinement.
The function returns a single-row 2D list containing the optimal variable values followed by the objective value, or an error message string if validation fails.
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:
- →
"(x[0]-1)**2 + (x[1]+2)**2"or"(x[0]-1)^2 + (x[1]+2)^2" - →
"x[0]**2 + x[1]**2" - →
"sin(x[0]) + cos(x[1])" - →
"x[0]**4 - 2*x[0]**2 + 2*x[1]**2 + x[2]**2"
Examples
Example 1: Quadratic Bowl with Powell Refinement
Inputs:
| func_expr | bounds | ns | finish | |
|---|---|---|---|---|
| (x[0]-1)**2 + (x[1]+2)**2 | -3 | 3 | 25 | fmin_powell |
| -3 | 3 |
Excel formula:
=BRUTE("(x[0]-1)**2 + (x[1]+2)**2", {-3,3;-3,3}, 25, "fmin_powell")Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 1.000 | -2.000 | 0.000 |
This example uses a non-default finish method for local refinement.
Example 2: Skipping Local Refinement
Inputs:
| func_expr | bounds | ns | finish | |
|---|---|---|---|---|
| (x[0]-2)**2 + (x[1]-1)**2 | -4 | 4 | 15 | (blank) |
| -4 | 4 |
Excel formula:
=BRUTE("(x[0]-2)**2 + (x[1]-1)**2", {-4,4;-4,4}, 15, )Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 2.286 | 1.143 | 0.102 |
Example 3: Three Variables
Inputs:
| func_expr | bounds | ns | ||
|---|---|---|---|---|
| (x[0]+1)**2 + (x[1]-0.5)**2 + (x[2]-2)**2 | -2 | 2 | 12 | |
| -2 | 2 | |||
| 0 | 4 |
Excel formula:
=BRUTE("(x[0]+1)**2 + (x[1]-0.5)**2 + (x[2]-2)**2", {-2,2;-2,2;0,4}, 12)Expected output:
| x₁ | x₂ | x₃ | Objective |
|---|---|---|---|
| -1.000 | 0.500 | 2.000 | 0.000 |
Example 4: Powell Refinement
Inputs:
| func_expr | bounds | ns | finish | |
|---|---|---|---|---|
| x[0]**2 + x[1]**2 | -5 | 5 | 10 | fmin_powell |
| -5 | 5 |
Excel formula:
=BRUTE("x[0]**2 + x[1]**2", {-5,5;-5,5}, 10, "fmin_powell")Expected output:
| x₁ | x₂ | Objective |
|---|---|---|
| 0.000 | 0.000 | 0.000 |
Python Code
import math
from typing import List, Optional
import numpy as np
from scipy.optimize import brute as scipy_brute
from scipy.optimize import fmin as scipy_fmin
from scipy.optimize import fmin_powell as scipy_fmin_powell
def brute(
func_expr: str,
bounds: List[List[float]],
ns: int = 20,
finish: Optional[str] = 'fmin',
):
"""
Perform a brute-force grid search to approximate the global minimum of a function.
This function wraps scipy.optimize.brute to perform global optimization over a specified domain.
For more information, see: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brute.html
Args:
func_expr: Objective expression using `x[i]` for variable access.
bounds: 2D list of [min, max] pairs describing each variable's domain.
ns: Number of grid points per dimension (must be >= 2).
finish: Optional local optimizer applied to the best grid point. Choices: 'fmin', 'fmin_powell', None.
Returns:
list[list[float]] or str: [[x1, x2, ..., objective]] on success, otherwise an error message string.
This example function is provided as-is without any representation of accuracy.
"""
# Validate func_expr
if not isinstance(func_expr, str):
return "Invalid input: func_expr must be a string."
if 'x' not in func_expr:
return "Invalid input: func_expr must reference variable x (e.g., x[0])."
# Validate bounds
if not isinstance(bounds, list) or len(bounds) == 0:
return "Invalid input: bounds must be a 2D list of [min, max] pairs."
slices = []
for idx, bound in enumerate(bounds):
if not isinstance(bound, list) or len(bound) != 2:
return "Invalid input: each bound must be a [min, max] pair."
try:
lower = float(bound[0])
upper = float(bound[1])
except (TypeError, ValueError):
return "Invalid input: bounds must contain numeric values."
if lower > upper:
return f"Invalid input: lower bound must not exceed upper bound for variable index {idx}."
slices.append(slice(lower, upper, complex(0, ns)))
dimension = len(slices)
# Validate ns
try:
ns = int(ns)
except (TypeError, ValueError):
return "Invalid input: ns must be an integer."
if ns < 2:
return "Invalid input: ns must be at least 2."
# Validate finish option
finish_map = {
'fmin': scipy_fmin,
'fmin_powell': scipy_fmin_powell,
None: None,
'none': None,
}
if finish not in finish_map:
return "Invalid input: finish must be one of 'fmin', 'fmin_powell', or None."
finish_callable = finish_map[finish]
# Objective evaluation
def objective(x_vector):
try:
local_x = [float(val) for val in np.atleast_1d(x_vector)]
value = eval(func_expr, {"x": local_x, "math": math, "np": np})
except Exception as exc:
raise ValueError(f"Error evaluating func_expr: {exc}")
try:
result_value = float(value)
except (TypeError, ValueError):
raise ValueError("Objective expression must return a scalar numeric value.")
if math.isnan(result_value) or math.isinf(result_value):
raise ValueError("Objective evaluation produced NaN or infinity.")
return result_value
try:
result = scipy_brute(
objective,
ranges=slices,
Ns=ns,
full_output=True,
finish=finish_callable,
disp=False,
)
except ValueError as exc:
return f"Error during brute optimization: {exc}"
except Exception as exc:
return f"Error during brute optimization: {exc}"
if not isinstance(result, tuple) or len(result) < 2:
return "Brute optimization failed: unexpected result structure."
solution_vector, objective_value = result[0], result[1]
try:
solution_list = [float(val) for val in np.atleast_1d(solution_vector)]
except (TypeError, ValueError):
return "Error converting solution vector to floats."
objective_value = float(objective_value)
return [solution_list + [objective_value]]