scijit.optimize.brute

scijit.optimize.brute(func, ranges, args=(), Ns=20, full_output=0, finish=<function fmin>, disp=False, workers=1, xtol=0.0001, ftol=0.0001)

Brute-force grid search over a full Cartesian grid.

Evaluates func on a full Cartesian grid and returns the best point, optionally polished by a local minimiser. No randomness is involved: the same call gives the same answer every time.

Callback style Afunc is a plain @njit func(x, *args) -> float64.

Parameters:
funccallable

A plain @njit f(x, *args) -> float.

ranges(N, 2) array_like

One (min, max) pair per dimension. slice objects are not accepted; see Notes.

argstuple or ndarray, optional

Extra parameters. A tuple is unpacked into separate arguments after x, so args=(a, b) calls func(x, a, b). Its elements may be of any type a compiled call accepts, arrays and strings included. Default (), which calls func(x). An ndarray or a list arrives as ONE argument instead, func(x, args). See Notes.

Nsint, optional

Points per axis. Default 20.

full_outputbool, optional

False (default) returns x0 alone. True returns (x0, fval, grid, Jout). Compile-time constant inside @njit.

finishcallable or None, optional

The minimiser that polishes the best grid point. None skips it; fmin() (default) runs Nelder-Mead. Any other minimiser raises. Resolved when the call compiles, so it is a name at the call site rather than a variable. See Notes.

dispbool, optional

Reaches the finish minimiser, which then prints its own summary, and turns on a RuntimeWarning for a polish that reports a non-zero status. Default False.

workersint, optional

1 (default) evaluates the grid in order on one thread. Any other value evaluates it in a prange; -1 selects all cores. The result is BIT-IDENTICAL either way: the grid points are independent and nothing is reduced across them.

The VALUE does not cap the thread count; numba’s own NUMBA_NUM_THREADS or numba.set_num_threads(k) does. Compile-time literal, because prange needs parallel=True at compile time. A map-like callable in workers is not accepted; see Notes.

Worth it only when the grid is large enough to cover the fixed cost of entering a parallel region: a few hundred microseconds. A 20x20 grid of a cheap objective is slower in parallel.

xtol, ftolfloat, optional

Passed to the finish minimiser. Both default to 1e-4. See Notes.

Returns:
x0ndarray, shape (N,)

Minimizer – the best grid point, or the polished point.

fvalfloat

func at x0. With full_output.

gridndarray, shape (M, N)

Every grid point, C order. With full_output. Reshape to (N,) + (Ns,)*N with grid.T.reshape((N,) + (Ns,)*N); see Notes.

Joutndarray, shape (M,)

func at every grid point. With full_output. Reshape with Jout.reshape((Ns,)*N).

Raises:
ValueError

An empty ranges, ranges that is not (N, 2), more than 40 variables, Ns < 1, a grid of more than 2**31 points, a finish that is neither None nor fmin(), or a non-integer workers. ranges, finish and workers raise from Python; inside @njit finish raises the same ValueError while workers and full_output raise TypingError carrying the same message, since both are read when the call compiles.

See also

scipy.optimize.brute

The scipy routine this mirrors.

scijit.optimize.differential_evolution

Stochastic, no grid.

scijit.optimize.basinhopping

Random restarts around a local minimizer.

scijit.optimize.fmin

The polish step finish=fmin runs.

Notes

finish takes two spellings, None and the minimiser itself, from Python and from inside @njit. A minimiser other than fmin() raises, and so does one chosen at RUN TIME, since the name is read when the call compiles.

UNAVAILABLE: slice objects in ranges, which take (min, max) pairs instead.

UNAVAILABLE: a grid whose ndim follows N. It and Jout are flat in C order, since a compiled function cannot return an array whose ndim depends on a runtime value.

UNAVAILABLE: a map-like callable in workers. It is an int literal, and any other value raises.

Ns < 1 and a grid above 2**31 points raise before the grid is built. scipy builds a zero-point grid and fails later inside argmin, with numpy’s message.

DELIBERATELY DIFFERENT: N == 1 with finish=None returns a length-1 array where scipy returns a float. A numba function has one return type and N is a runtime value. With a polish scipy also returns an array, so the two agree there.

xtol and ftol reach the finish minimiser and both default to 1e-4.

args is unpacked into the objective’s argument list, func(x, *args).

DELIBERATELY DIFFERENT: an ndarray or a list args reaches func as ONE argument, func(x, args). scipy unpacks those two element by element. The arity of a compiled call is fixed when it compiles, so a sequence whose length is known only at run time cannot be unpacked: func(x, *args) on an ndarray inside @njit is a TypingError. An objective written to the unpacked shape refuses the ndarray rather than reading it wrongly.

https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brute.html

Examples

>>> import numpy as np
>>> from numba import njit
>>> from scijit.optimize import brute
>>> @njit
... def q(x):
...     return (x[0] - 1.5) ** 2 + (x[1] + 0.5) ** 2
>>> ranges = np.array([[-3.0, 3.0], [-3.0, 3.0]])
>>> @njit
... def run():
...     return brute(q, ranges)
>>> np.round(run(), 4)
array([ 1.5, -0.5])