Skip to content

Simple global optimizers and models, including RF filters, together with stats generating scripts and interactive OpenGL visualizations

License

Notifications You must be signed in to change notification settings

m4r35n357/VisualOptimizers

Repository files navigation

Simple Visual Gradient-free optimizers

Here are some basic global optimizers, for experimentation and learning through visualization. If you are unsure what the "Curse of Dimensionality" means, or "No Free Lunch", this project might help.

There are currently four candidate algorithms included (two good, two not so good!), each with an interactive OpenGL visualizer:

  • Nelder-Mead - features a "multi-run" bulk mode for global optimization, using an adaptive algorithm, and sensible simplex initialization
  • Optimization by Cut (including the published algorithm, my simplified & improved version, and a completely random optimizer!)

The methods are suitable for global optimization in up to 16 dimensions with the cut algorithms, and scenarios up to 64 dimensions for "bulk-mode" Nelder-Mead.

Now includes "real-world" RF filter synthesis examples, with electrical component values as the parameter space (variables).

Firstly there is a multi-order Butterworth model, which is optimized against the theoretical maximally-flat frequency response (an explicit, closed-form solution). Against this ideal, the error can theoretically be optimized down to zero (subject to numerical errors at higher filter order).

Secondly, there are two low-pass notch designs, to be optimized against a regular filter specification. In this case, there is no explicit solution, and if the spec is exceeded (the minimum hits zero), there is a continuum of solutions.

Pure c99 (plus optional 3D OpenGL visualization)

The code is concise (currently < 1400 LOC) and efficient, and produces tiny executables (~ 20-40KB). The programs can be built with either Clang or GCC. All console programs are written to and depend only on the c99 standard and library (strictly speaking, the WG14/N1256 draft standard!). External dependencies (OpenGL, FreeGLUT & GLEW) are only needed for the OpenGL plotters (*-gl).

All simulation floating point operations are executed in long double precision. This gives a choice of precision and performance on different platforms.

Platform Long Double Implementation
x86 / x86-64 80 bit hardware float
armhf 64 bit hardware float
aarch64 128 bit software float

These are converted to float for use by OpenGL.

All scripts are in Posix shell syntax.

Quick Start

Requirements - Debian/Ubuntu packages

sudo apt install bc git build-essential freeglut3-dev glew-utils libglew-dev

Download

git clone https://github.com/m4r35n357/VisualOptimizers
cd VisualOptimizers

c Build (Clang with debug by default)

make depclean

once only, unless you have edited any header files, then either of

make
make nogl

There should be NO errors or warnings. The default build requires OpenGL development libraries, and produces the *-gl executables. The nogl build is pure c99 and produces the *-std executables. C tags are built automatically, which can make things more comfortable in some editors and IDEs. Also, there is a pre-commit script for use with git, which rebuilds everything and runs some simple scenarios. To run it:

./pre-commit

To install it:

cp pre-commit .git/hooks

The default make command enables debugging and uses optimization level 0. It is good for use with IDEs, and is also used by the commit script. Fast executables (using -O3 -fno-math-errno -flto) can also be built, and make a big difference with "bulk" running (e.g. multi-run Nelder-Mead, and particularly the stats script):

make CCC=gcc
make CCC=clang

See the Makefile for various additional "test" targets. In particular:

make test-3d

shows basic program output (with stdout suppressed!) and can be used to get OpenGL commands by cut & paste - just change std to gl in the program names.

The "multi" target runs stats:

make CCC=gcc test-multi-3-16

Performs 3D, 8D and 16D stats runs using the Nelder-Mead and (unclamped) cut algorithms.

make CCC=gcc test-multi-16-64

Performs 16D, 32D, and 64D stats runs using Nelder-Mead only. Taken together, these two "multi" targets produce a good overall performance summary of the various algorithms and models.

make CCC=gcc test-filters

Synthesizes RF filter components for various orders of filter.

make CCC=gcc test-multi-filters

Some stats runs for the Butterworth and "notch" LC filters for various orders.

Usage

The make tests above show summarized output for models of interest. All programs and scripts output their command arguments for easy cut & paste (magenta for scripts, cyan for binaries). The commands and their arguments are detailed below.

Nelder-Mead

Nelder, John A.; R. Mead (1965). "A simplex method for function minimization". Computer Journal. 7 (4): 308–313. doi:10.1093/comjnl/7.4.308.

This code was originally based on a project by Matteo Maggioni in 2023. It has since been extensively rewritten, now uses regular simplexes, and has a "multi-run" (or "bulk") mode for use as a global optimizer.

Regular vertex coordinates are generated using an algorithm which is described (open access) here. The adaptive algorithm is described (open access) here. It is used automatically when the number of dimensions exceeds four.

Parameter Meaning
1 Display precision (1..36)
2 Floating point format (fixed / exp)
3 Number of dimensions
4 Maximum error for convergence
5 Number of evaluations (maximum per iteration, and also the total in bulk mode)
6 Initial simplex scale
7 Initialization (point / random / bulk) - ignored for GL

If Initialization == point

Parameter Meaning
8+ Coordinates of starting point

If Initialization == random or bulk

Parameter Meaning
8 Lower limit
9 Upper limit

Examples

./nm-trid-std 3 fixed 3 1.0e-6 2627 10.0 non-adaptive bulk -10 10 >/dev/null
./nm-trid-gl 3 fixed 3 1.0e-6 2627 10.0 non-adaptive random -10 10 

When parameter 8 is set to a non-zero value, the algorithm is run several times until the cumulative number of evaluations exceeds this value (budget). This enables meaningful comparisons with the other methods.

The OpenGL visualization shows two "dual" regular initial simplexes. For each one, the best vertex is green, and the worst is red. The remaining vertices are coloured either cyan (default simplex) or gold (dual simplex), correspondiing to the OSD text colour.

Optimization by Cut & Random Optimization

Algorithm described here.

There is one key modification that I have made; instead of "clamping" the new box to be entirely within the previous one, I allow the box to be centered around the new minimum, giving a degree of movement to the box that would otherwise be absent. I have found experimentally that this improves success rates in all cases of interest. I retain the published "clamping" mechanism to confine the box to within the original boundary.

The algorithm has now been updated with a random optimizer, which is identical to the cut algorithms except that the box is never shrunk!

Use the OpenGL visualizations to see how this all works.

Parameter Meaning
1 Display precision (1..36)
2 Floating point format (fixed / exp)
3 Number of dimensions
4 Number of search agents
5 Number of iterations
6 *Algorithm mode (unclamped / clamped / random)
7 Lower limit
8 Upper limit
  • ignored in OpenGL, which displays all modes

Examples

./cut-trid-std 3 fixed 3 27 100 unclamped -10 10 >/dev/null
./cut-trid-gl 3 fixed 3 27 100 unclamped -10 10 

The OpenGL visualization shows both of the cut as well as the random algorithms regardless of parameter 6. Clamped mode (the published method) is represented by green particles in a red box, with the best point marked in red. Unclamped mode (my modification, which allows the box a degree of movement) is represented by cyan particles in a magenta box, with the best point marked in magenta. Random mode is represented by gold particles, with the best point marked in yellow.

The Models

These are the most "important", in decreasing order

Model Justification
e3, e5 low-pass notch filter design from specifications (passband ripple, selectivity, stopband loss)
bw symmetric Butterworth RF low-pass filter design (filter order = 2 * dimension - 1)
trid slightly more involved than the well-known sphere model, but still unimodal and well behaved
rosenbrock unimodal, non-convex, tests ability to cope with contrasting directional slopes
easom unimodal, non-convex, "needle in a haystack". Also tests machine precision!

Filters

These commands will provide some cut & paste examples:

make CCC=gcc test-filters
make CCC=gcc test-multi-filters

"solve-model" script

Run a single model against all integrators, including random

Parameter Meaning
1 Model name
2 Number of dimensions
3 Number of search agents
4 Number of iterations
5 Lower limit
6 Upper limit

Parameters 3 and 4 are multiplied within the script to give a "budget" of evaluations for Nelder-Mead in bulk mode.

Example

make clean
make CCC=gcc
./solve-model easom 8 256 100 -15 15

To get a clearer understanding of the run-by-run variation in performance of the various integrators, use the "stats" script below.

"stats" script

Runs a single algorithm multiple times agains a target value for a single model.

Parameter Meaning
1 Number of runs
2 Threshold
3+ optimizer command

For each run, the runs/iterations/evaluations counters are green if the result is below the user-supplied threshold, and red otherwise. Total number of passes is printed at the end, colour coded as:

Colour Percentage
green > 90%
cyan 70% -> 90%
orange/brown 20% -> 70%
red < 20%

Examples

make clean
make CCC=gcc
./stats 100 0.001 ./nm-trid-std 3 fixed 8 1.0e-6 25756 10.0 adaptive random -64 64
./stats 100 0.03 ./cut-trid-std 3 fixed 8 256 100 unclamped -64 64

For 8D or higher, using one of the faster "CCC=" make options above is highly recommended!

"Global" Optimization

A common feature of particle-based methods is an exploration phase followed by a refinement phase (in practice the transition is a gradual process). Refinement is not the same thing as convergence; you get what you are given after a specified number of iterations! Another way of looking at it is that the initial search domain is shrunk by a large factor, concentrating the search agents into a relatively small volume around a potential minimum. In any case, this refinement stage makes it harder to jump out of a stubborn local minimum.

I have adapted the Nelder-Mead method with an option to do a series of random runs, while keeping the best result, and accounting for total number of function evaluations. The size of the initial simplex is set to a large value, and the adaptive setting is used for 8D and above; this setup works rather well as a global optimizer! This is because the random exploration is not subject to any refinement process so global minima are always potentially accessible, even if not actually reached within the set limits.

The comparisons are roughly equivalent in that the number of Nelder-Mead evaluations is set roughly to (agents) x (iterations), which is a good approximation to the number of evaluations used by the particle-based optimizers. Do your own experiments!

"multi-stats" script

Runs the "stats" script multiple times for all algorithms against a single model, to help even out the typically large fluctuations in results from run to run, and get a more realistic view on comparative performance of the algorithms. Total passes from the stats script are printed for each algorithm.

Parameter Meaning
1 number of runs
2 threshold
3 model name
4 number of dimensions
5 number of search agents
6 number of iterations per run
7 range min
8 range max

Parameters 5 and 6 are multiplied within the script to give a "budget" of evaluations for Nelder-Mead.

Examples

make clean
make CCC=gcc
./multi-stats 100 0.001 trid 8 256 1000 -64 64

See the Makefile for other examples.

Interactive OpenGL Visualizations

To get a list of 3D OpenGL command examples for cut & paste into a terminal, use one of the commands below:

make test-3d 2>&1 | grep std | sed 's/^.*\[ //' | sed 's/-std/-gl/' | sed 's/[ ]*\]//' | grep nm
make test-3d 2>&1 | grep std | sed 's/^.*\[ //' | sed 's/-std/-gl/' | sed 's/[ ]*\]//' | grep cut

The visualizations also "work" for more than three dimensions, obviously they do not tell the whole story in this case, but can still give some kind of indication of progress. For example, try this (it will take a litttle longer to run than the 3D case):

make test-8d 2>&1 | grep std | sed 's/^.*\[ //' | sed 's/-std/-gl/' | sed 's/[ ]*\]//' | grep cut

Coordinates above the third are projected into the 3D space (ignored!).

OpenGL Keyboard Controls

The normal mode of use is to manually step through the iteration sequence using the "s" key.

The global minimum is no longer displayed by default, use the 'm' key to show it at any time. For a more immersive "black-box" exerience, leave it until the end!

Key Action
UP arrow increase view elevation
DOWN arrow decrease view elevation
LEFT arrow increase view azimuth
RIGHT arrow decrease view azimuth
HOME zoom in (fine)
END zoom out (fine)
PAGE UP zoom in (coarse)
PAGE DOWN zoom out (coarse)
'C' 'c' show/hide centroid (for Nelder-Mead only)
'M' 'm' show/hide global minimum
'G' 'g' increase ball size
'B' 'b' decrease ball size
'S' 's' iterate
'F' 'f' toggle fullscreen mode
'H' 'h' toggle OSD text
'Q' 'q' ESC exit

Examples

./nm-trid-gl 3 fixed 3 1.0e-6 10000 10.0 non-adaptive random -10 10 
./cut-trid-gl 3 fixed 3 27 100 unclamped -10 10 

About

Simple global optimizers and models, including RF filters, together with stats generating scripts and interactive OpenGL visualizations

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published