npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

optimization-test-functions

v1.0.0

Published

Canonical global-optimization benchmarks and classical psychophysical laws, with per-axis domains and known optima. Zero dependencies.

Readme

optimization-test-functions

Standard global-optimization benchmarks and classical psychophysical laws, in JavaScript. No dependencies, no build step, ESM.

Every function ships with the metadata you need to use it correctly: the domain it is actually defined on, its known optimum, and which direction is the interesting one.

npm install optimization-test-functions
import { ackley, branin, domain, negate } from 'optimization-test-functions';

ackley([0, 0, 0]);            // 0        — the global minimum
branin([-Math.PI, 12.275]);   // 0.397887 — one of three equal minima

domain('branin');             // [[-5, 10], [0, 15]]  — note the asymmetry
domain('ackley', 4);          // four copies of [-32.768, 32.768]

const maximise = negate(ackley);
maximise.meta.sense;          // 'max'

Why this exists

There was no JavaScript implementation of these. Python has BoTorch, DEAP and several standalone packages; the npm registry had nothing, so anyone writing an optimizer for the browser was retyping Hartmann's constant tables by hand.

Two things this gets right that hand-ported versions usually do not:

Domains are per-axis. Branin is defined on x₁ ∈ [-5, 10], x₂ ∈ [0, 15]. Applying one range to both axes is a common and completely silent mistake — the function still evaluates, it just is not Branin any more.

Nothing is silently negated. The optimization benchmarks are minimized, as their sources define them. The psychophysical laws are given in the sense the law states — Hick–Hyman returns a reaction time, so lower is better, and meta.sense says so. If your optimizer maximizes, wrap with negate.

What's included

Classical optimization benchmarks — minimized

| function | dims | domain | minimum | |---|---|---|---| | ackley | any | [-32.768, 32.768]ᵈ | 0 at the origin | | griewank | any | [-600, 600]ᵈ | 0 at the origin | | schwefel | any | [-500, 500]ᵈ | 0 at (420.9687, …) | | eggholder | 2 | [-512, 512]² | -959.6407 | | powell | multiple of 4 | [-4, 5]ᵈ | 0 at the origin | | shekel | 4 | [0, 10]⁴ | -10.5364 | | hartmann3 | 3 | [0, 1]³ | -3.86278 | | hartmann6 | 6 | [0, 1]⁶ | -3.32237 | | branin | 2 | [-5, 10] × [0, 15] | 0.397887, three times | | rosenbrock | ≥ 2 | [-5, 10]ᵈ | 0 at all-ones | | rastrigin | any | [-5.12, 5.12]ᵈ | 0 at the origin | | michalewicz | any | [0, π]ᵈ | d-dependent |

Classical psychophysical laws

Human response functions rather than optimization benchmarks. Three of the four are monotone, so on any box their extremum sits on the boundary — weak as search problems, but a landscape class the benchmarks above do not contain, and one some acquisition functions handle badly.

| function | law | sense | |---|---|---| | yerkesDodson | inverted-U of arousal against performance (1908) | max, interior | | stevens | ψ = I^0.67, Stevens' power law (1957) | max, boundary | | hickHyman | RT = a + b·log₂(n+1), Hick (1952) / Hyman (1953) | min, boundary | | weberFechner | ψ = ln(1 + I/I₀), Fechner (1860) | max, boundary |

Metadata

import { shekel } from 'optimization-test-functions';

shekel.meta;
// {
//   name: 'Shekel', dims: 4, sense: 'min',
//   domain: d => [[0, 10], [0, 10], [0, 10], [0, 10]],
//   optimum: {
//     value: -10.53644315348353,
//     at: () => [4.0007468671, 3.9995094806, 4.00074687, 3.9995094776],
//     approx: [4, 4, 4, 4],
//     note: 'for m = 10',
//   },
//   reference: 'Shekel, J. (1971). …',
// }

Shekel is a good example of why the metadata is worth having. Its minimum is almost always quoted at (4, 4, 4, 4), but that is only approximate — the neighbouring wells pull it slightly off, and f(4,4,4,4) = -10.536283, not -10.536443. Both the quoted and the refined location are recorded.

Optional parameters

shekel(x, { m: 5 });                  // fewer wells
michalewicz(x, { m: 1 });             // gentler ridges (default 10)
ackley(x, { a: 20, b: 0.2, c: 2 * Math.PI });
branin(x, { a: 1, b: 5.1 / (4 * Math.PI ** 2), /* … */ });
yerkesDodson(x, { peak: 0.5, width: 0.15 });
hickHyman(x, { a: 0.2, b: 0.15 });

Helpers

import { functions, optimizationBenchmarks, psychophysicalLaws,
         domain, fromUnitCube, negate } from 'optimization-test-functions';

functions.ackley([1, 2]);                  // registry, keyed by name
optimizationBenchmarks;                    // the 12 classical names
psychophysicalLaws;                        // the 4 human response names
fromUnitCube('branin', [0.5, 0.5]);        // [2.5, 7.5]

fromUnitCube matters more than it looks: most optimizers work on the unit cube, and mapping back with a single shared range is exactly how Branin gets evaluated on the wrong box.

Correctness

Two independent checks, both run in CI:

  • npm test — 11 suites. Verifies that every stated optimum is actually attained at its stated location, and that 200 000 random samples inside each domain fail to beat it. Also covers dimension validation, negate, and the per-axis domain mapping.

  • python test/crosscheck_botorch.py — compares 308 values across 11 functions against BoTorch's test_functions.synthetic, the reference implementation most of the Bayesian-optimization field uses.

    | function | max relative difference | |---|---| | ackley, eggholder, powell, rosenbrock, rastrigin | 0 exactly | | griewank | 3.5e-16 | | branin | 5.5e-16 | | michalewicz | 2.2e-15 | | shekel | 5.8e-09 | | hartmann6 | 1.2e-08 | | hartmann3 | 2.9e-08 |

    Nothing exceeds 3e-08, and everything above machine epsilon is BoTorch's own doing: it stores ALPHA, A and C as float32 and upcasts, so its constants carry about 1e-8 of rounding while the values here are exact float64. That is why the tolerance is 1e-6 and not tighter — tightening it fails on BoTorch's precision, not on ours.

Schwefel is not in BoTorch, and the psychophysical laws are not optimization benchmarks, so those five are checked by the JS suite only.

Notes

ESM only. Node 18+. For CommonJS, use a dynamic import:

const { ackley } = await import('optimization-test-functions');

TypeScript types are hand-written and shipped alongside — there is no build step, so what you install is what is in src/.

License

MIT