Skip to content

fitminimizers

Downhill simplex optimisation, with simulated annealing, for Delphi and Lazarus

View on GitHub

Contour lines of an error landscape and a downhill simplex closing in on its minimum

The downhill simplex optimisation algorithm for Delphi and Lazarus, in two variants — the classical one and a version with simulated annealing — packaged so that the algorithm knows nothing about the problem it is solving.

Written for Fit, and used by MotifMASTER as well. Runs on Windows, Linux and macOS.

Licence

MPL-2.0 — see LICENSE. The Mozilla Public License is file-level: a program of any licence, free or commercial, may use these components, and changes to their own files stay under MPL-2.0 with their source available.

What is in it

Component What it does State
TDownhillSimplexAlgorithmThe classical downhill simplex.Implemented
TDownhillSimplexSAAlgorithmThe same, with simulated annealing, for a surface with more than one minimum.Implemented
TDownhillSimplexServerThe container a host talks to, wrapping the algorithm as a component.Implemented
TCombEnumerator, TCombSelectorCombinatorial enumeration of discrete parameter values.Implemented
TRunningThread, TRunner, TRunnerPoolBackground execution and a pool for running independent optimisations in parallel.Implemented

The algorithm is abstracted from any particular task. The host implements IDownhillSimplexServer — it supplies the parameters and evaluates the goal function — and the algorithm never learns what they mean.

RunningThread does not depend on the LCL: it calls an injectable idle hook (OnProcessMessages) instead of the widget set, so a console program can use the runner pool without linking a UI.

Class diagram

classDiagram
    direction LR
    class TComponent["TComponent"]
    <<external>> TComponent
    class TComponentList["TComponentList"]
    <<external>> TComponentList
    class TObject["TObject"]
    <<external>> TObject
    class TThread["TThread"]
    <<external>> TThread
    class IDiscretValue["IDiscretValue"]
    <<interface>> IDiscretValue
    class IDownhillRealParameters["IDownhillRealParameters"]
    <<interface>> IDownhillRealParameters
    class IDownhillSimplexParameters["IDownhillSimplexParameters"]
    <<interface>> IDownhillSimplexParameters
    class IDownhillSimplexServer["IDownhillSimplexServer"]
    <<interface>> IDownhillSimplexServer
    class ILevenbergMarquardtServer["ILevenbergMarquardtServer"]
    <<interface>> ILevenbergMarquardtServer
    class IOptimizedFunction["IOptimizedFunction"]
    <<interface>> IOptimizedFunction
    class IOptimizedResiduals["IOptimizedResiduals"]
    <<interface>> IOptimizedResiduals
    class IUpdatingResults["IUpdatingResults"]
    <<interface>> IUpdatingResults
    class TAbstractDecision["TAbstractDecision"]
    class TAlgorithm["TAlgorithm"]
    class TAlgorithmContainer["TAlgorithmContainer"]
    class TByteDecision["TByteDecision"]
    class TCombEnumerator["TCombEnumerator"]
    class TCombSelector["TCombSelector"]
    class TDecisionsList["TDecisionsList"]
    class TDownhillSimplexAlgorithm["TDownhillSimplexAlgorithm"]
    class TDownhillSimplexDecision["TDownhillSimplexDecision"]
    class TDownhillSimplexSAAlgorithm["TDownhillSimplexSAAlgorithm"]
    class TDownhillSimplexSADecision["TDownhillSimplexSADecision"]
    class TDownhillSimplexServer["TDownhillSimplexServer"]
    class TFloatDecision["TFloatDecision"]
    class TLevenbergMarquardtAlgorithm["TLevenbergMarquardtAlgorithm"]
    class TLevenbergMarquardtServer["TLevenbergMarquardtServer"]
    class TOneDimDecision["TOneDimDecision"]
    class TRunner["TRunner"]
    class TRunnerPool["TRunnerPool"]
    class TRunningThread["TRunningThread"]
    class TThreadAlgorithmContainer["TThreadAlgorithmContainer"]
    class TTwoDimDecision["TTwoDimDecision"]
    class TTwoDimFloatDecision["TTwoDimFloatDecision"]
    TComponent <|-- TAbstractDecision
    TComponent <|-- TAlgorithm
    TComponent <|-- TAlgorithmContainer
    TOneDimDecision <|-- TByteDecision
    TCombEnumerator <|-- TCombSelector
    TComponentList <|-- TDecisionsList
    TAlgorithm <|-- TDownhillSimplexAlgorithm
    TFloatDecision <|-- TDownhillSimplexDecision
    TDownhillSimplexAlgorithm <|-- TDownhillSimplexSAAlgorithm
    TDownhillSimplexDecision <|-- TDownhillSimplexSADecision
    TAlgorithmContainer <|-- TDownhillSimplexServer
    IDownhillSimplexServer <|.. TDownhillSimplexServer
    TOneDimDecision <|-- TFloatDecision
    TAlgorithm <|-- TLevenbergMarquardtAlgorithm
    TDownhillSimplexServer <|-- TLevenbergMarquardtServer
    ILevenbergMarquardtServer <|.. TLevenbergMarquardtServer
    TAbstractDecision <|-- TOneDimDecision
    TComponent <|-- TRunner
    TObject <|-- TRunnerPool
    TThread <|-- TRunningThread
    TAlgorithmContainer <|-- TThreadAlgorithmContainer
    TAbstractDecision <|-- TTwoDimDecision
    TTwoDimDecision <|-- TTwoDimFloatDecision

22 classes and 8 interfaces are declared in package/, read from the units themselves rather than from a diagram kept by hand.

Demo applications

The example problem is the minimum bounding box: given a set of 3D points, find the smallest box that encloses them, by varying rotation angles. See minimum bounding box and bounding volume. Many thanks to Ingo Wulf for contributing to the development and providing the test models.

There are three, sharing the code in examples/Common/:

ExampleShows
examples/BoundingBoxServerForm/Supplying the algorithm with data through an interface, and optimising independent tasks in parallel.
examples/BoundingBoxComponentForm/Using the form component to run a long optimisation on its own thread, leaving the application responsive.
examples/BoundingBoxConsole/The same optimisation with no UI at all — the case the LCL decoupling exists for.

The bounding-box demo, with the model and the fitted box

The component demo, optimising on a background thread

Using it

The package is package/FitMinimizers.lpk for Lazarus and package/FitMinimizers.dpk for Delphi. Open it in the IDE and compile. The examples are ordinary projects beside it — bounding_box_cli.lpi in examples/BoundingBoxConsole/, and one .lpi in each of the two form examples.