Documentation
¶
Overview ¶
Package dragonfly implements the Dragonfly Algorithm (DA), a swarm intelligence metaheuristic modeled on the static and dynamic swarming behavior of dragonflies.
A run maintains a population of dragonflies, each carrying a position X and a step ΔX (the velocity analog). Every iteration each dragonfly combines five primitives -- separation, alignment, cohesion, attraction to the food source (the best position seen) and distraction from the enemy (the worst position seen) -- into a new step, and adds that step to its position. Adaptive weight schedules move the swarm from exploration to exploitation as the run progresses; a dragonfly that is isolated and out of reach of the food source performs a Lévy random walk instead.
The entry points are Optimize and OptimizeContext. Configure a run with NewDefaultConfig (or one of the other factories in config.go) and set ObjectiveFunc, ProblemSize, LowerBound and UpperBound.
Reference: Mirjalili, S. (2016). Dragonfly algorithm: a new meta-heuristic optimization technique for solving single-objective, discrete, and multi-objective problems. Neural Computing and Applications, 27(4), 1053-1073. DOI: 10.1007/s00521-015-1920-1
Go implementation by Christian-W. Budde.
Index ¶
- Constants
- Variables
- func Ackley(x []float64) float64
- func AutoTuneConfig(config *Config)
- func BenchmarkNames() []string
- func BentCigar(x []float64) float64
- func BetterConstrainedCandidate(candidate, incumbent CandidateEvaluation, config *ConstraintConfig) bool
- func BinaryPositionsValid(position []float64) bool
- func Discus(x []float64) float64
- func DixonPrice(x []float64) float64
- func ExpandedSchafferF6(x []float64) float64
- func Griewank(x []float64) float64
- func HappyCat(x []float64) float64
- func IsFeasible(violation float64) bool
- func Levy(x []float64) float64
- func ListPresets() map[ConfigPreset]string
- func ListVariants() []string
- func LookupTransferFunction(name TransferFunction) (func(float64) float64, error)
- func Michalewicz(x []float64) float64
- func PenalizedCost(cost, violation, factor float64, method PenaltyMethod) float64
- func PresetNames() []string
- func PrintPresets()
- func PrintRecommendations(recommendations []AlgorithmRecommendation)
- func Rastrigin(x []float64) float64
- func Rosenbrock(x []float64) float64
- func SaveConfig(config *Config, path string) error
- func SchafferN1(x []float64) []float64
- func Schwefel(x []float64) float64
- func Sphere(x []float64) float64
- func ValidateConfig(config *Config) error
- func VariantAliases() []string
- func Weierstrass(x []float64) float64
- func ZDT1(x []float64) []float64
- func ZDT2(x []float64) []float64
- func ZDT3(x []float64) []float64
- func Zakharov(x []float64) float64
- type AlgorithmRecommendation
- type AlgorithmSelector
- type AlgorithmStatistics
- type AlgorithmVariant
- type BDAVariant
- func (v *BDAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
- func (v *BDAVariant) Description() string
- func (v *BDAVariant) EstimatedOverhead() float64
- func (v *BDAVariant) FullName() string
- func (v *BDAVariant) GetConfig() *Config
- func (v *BDAVariant) IsMultiObjective() bool
- func (v *BDAVariant) Name() string
- func (v *BDAVariant) RecommendedFor() []string
- func (v *BDAVariant) Run(ctx context.Context, config *Config, options ...RunOption) (*Result, error)
- type Best
- type BoundaryMethod
- type CandidateEvaluation
- type ComparisonResult
- type ComparisonRunner
- func (cr *ComparisonRunner) Compare(benchmarkName string, fn ObjectiveFunction, problemSize int, ...) *ComparisonResult
- func (cr *ComparisonRunner) CompareContext(ctx context.Context, benchmarkName string, fn ObjectiveFunction, ...) (*ComparisonResult, error)
- func (cr *ComparisonRunner) WithIterations(iterations int) *ComparisonRunner
- func (cr *ComparisonRunner) WithMaxWorkers(workers int) *ComparisonRunner
- func (cr *ComparisonRunner) WithParallel(parallel bool) *ComparisonRunner
- func (cr *ComparisonRunner) WithRuns(runs int) *ComparisonRunner
- func (cr *ComparisonRunner) WithSeed(seed int64) *ComparisonRunner
- func (cr *ComparisonRunner) WithTarget(target float64) *ComparisonRunner
- func (cr *ComparisonRunner) WithVariantNames(names ...string) *ComparisonRunner
- func (cr *ComparisonRunner) WithVariants(variants ...AlgorithmVariant) *ComparisonRunner
- func (cr *ComparisonRunner) WithVerbose(verbose bool) *ComparisonRunner
- type Config
- type ConfigPreset
- type ConstraintConfig
- type ConstraintEvaluation
- type ConstraintFunction
- type ConstraintHandlingMethod
- type ConvergenceConfig
- type ConvergenceExport
- type ConvergencePoint
- type DAVariant
- func (v *DAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
- func (v *DAVariant) Description() string
- func (v *DAVariant) EstimatedOverhead() float64
- func (v *DAVariant) FullName() string
- func (v *DAVariant) GetConfig() *Config
- func (v *DAVariant) IsMultiObjective() bool
- func (v *DAVariant) Name() string
- func (v *DAVariant) RecommendedFor() []string
- func (v *DAVariant) Run(ctx context.Context, config *Config, options ...RunOption) (*Result, error)
- type Dragonfly
- type FriedmanTestResult
- type Landscape
- type Logger
- type MODAVariant
- func (v *MODAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
- func (v *MODAVariant) Description() string
- func (v *MODAVariant) EstimatedOverhead() float64
- func (v *MODAVariant) FullName() string
- func (v *MODAVariant) GetConfig() *Config
- func (v *MODAVariant) GetMultiObjectiveConfig() *MultiObjectiveConfig
- func (v *MODAVariant) IsMultiObjective() bool
- func (v *MODAVariant) Name() string
- func (v *MODAVariant) RecommendedFor() []string
- func (v *MODAVariant) Run(_ context.Context, _ *Config, _ ...RunOption) (*Result, error)
- func (v *MODAVariant) RunMultiObjective(ctx context.Context, config *MultiObjectiveConfig) (*MultiObjectiveResult, error)
- type Modality
- type MultiObjectiveConfig
- type MultiObjectiveFunction
- type MultiObjectiveResult
- type ObjectiveFunction
- type ParetoArchive
- type ParetoExport
- type ParetoPoint
- type ParetoSolution
- type PenaltyMethod
- type PopulationObserver
- type PopulationSnapshot
- type ProblemCharacteristics
- type Progress
- type ProgressObserver
- type Result
- type RunOption
- type RunResult
- type TerminationReason
- type TransferFunction
- type VariantBuilder
- func (b *VariantBuilder) Build() (*Config, error)
- func (b *VariantBuilder) ForProblem(fn ObjectiveFunction, size int, lower, upper float64) *VariantBuilder
- func (b *VariantBuilder) GetVariant() AlgorithmVariant
- func (b *VariantBuilder) Optimize() (*Result, error)
- func (b *VariantBuilder) OptimizeContext(ctx context.Context, options ...RunOption) (*Result, error)
- func (b *VariantBuilder) WithConfig(edit func(*Config)) *VariantBuilder
- func (b *VariantBuilder) WithIterations(iterations int) *VariantBuilder
- func (b *VariantBuilder) WithPopulation(size int) *VariantBuilder
- type WilcoxonResult
Examples ¶
Constants ¶
const ( // DefaultLevyBeta is the stability index used by the DA reference // implementation. β must lie in (0, 2]; at β = 2 the numerator's // sin(πβ/2) vanishes and σ collapses to zero, so β = 1.5 is the // practical heavy-tailed default. DefaultLevyBeta = 1.5 // DefaultLevyScale is the multiplicative step scale from the DA paper. DefaultLevyScale = 0.01 )
const ( // DefaultArchiveBeta is the exponent of the food-selection weight 1/N^beta. DefaultArchiveBeta = 4.0 // DefaultArchiveGamma is the exponent of the enemy-selection weight N^gamma. DefaultArchiveGamma = 2.0 // DefaultArchiveDelta is the exponent of the overflow-deletion weight N^delta. DefaultArchiveDelta = 2.0 // DefaultArchiveNGrid is the number of hypercubes per objective. DefaultArchiveNGrid = 10 // DefaultArchiveSize is the archive capacity. DefaultArchiveSize = 100 )
Default hypercube-grid parameters.
UNVERIFIED. These are the MOPSO defaults -- Coello Coello, Pulido & Lechuga (2004), the lineage MODA borrows its archive from -- and they have NOT been read off the author's MODA.m, which is not available to this repository. Do not cite them as settled values from the DA paper. Treat them as the working defaults they are until someone checks them against the reference source; see PLAN.md §1.7 and CLAUDE.md "Common Pitfalls" #5.
const DefaultTransferFunction = TransferV3
DefaultTransferFunction is the one BDA uses in the paper: the V-shaped v3. A Config that leaves TransferFunc empty runs with it.
const WeightAuto = -1.0
WeightAuto makes Optimize derive a swarming weight from the paper's adaptive schedule instead of taking the field literally. It is the default for every weight field, and it is a distinct sentinel rather than the zero value because zero is a meaningful weight -- "switch this behavior off" -- and a caller who wrote it must keep getting it.
It is the same convention Mayfly uses for NCAuto and AquilaWeightAuto.
Variables ¶
var ErrBinaryConfigOnContinuousVariant = errors.New(
"config has UseBinary set: run it through the BDA variant, not DA")
ErrBinaryConfigOnContinuousVariant is returned by DAVariant.Run when it is handed a configuration with UseBinary set.
Nothing in dragonfly.go dispatches on Config.UseBinary -- OptimizeContext documents that it ignores the field -- so a binary configuration handed to the continuous entry point runs the continuous algorithm on a swarm confined to [0,1] and returns a real-valued "solution" that is not a bit string. That is a silently wrong answer rather than a failure, so the variant layer, which is the one place that knows which algorithm the caller asked for, refuses it.
var ErrMultiObjectiveVariant = errors.New(
"multi-objective variant has no single-objective result; use RunMultiObjective")
ErrMultiObjectiveVariant is returned by AlgorithmVariant.Run for a variant whose IsMultiObjective reports true. MODA has no single incumbent to report, so it cannot produce a *Result; run it through MODAVariant.RunMultiObjective or OptimizeMultiObjective instead.
Functions ¶
func Ackley ¶
Ackley is the Ackley benchmark function: a nearly flat outer region with a deep central basin. Global minimum is at f(0, ..., 0) = 0.
func AutoTuneConfig ¶
func AutoTuneConfig(config *Config)
AutoTuneConfig adjusts swarm size and run length to the configured ProblemSize, in place. It is a handful of coarse heuristics, not a search: a tuned configuration for a particular objective will beat it.
Set ProblemSize (and ideally the bounds and ObjectiveFunc) before calling. A nil config, or one whose ProblemSize is not yet positive, is left alone -- there is nothing to tune against. Nothing here touches the five swarming weights: leaving them at WeightAuto keeps the paper's schedules, and pinning one is a deliberate choice a heuristic must not override.
func BenchmarkNames ¶
func BenchmarkNames() []string
BenchmarkNames returns every benchmark BenchmarkCharacteristics knows, in a stable alphabetical order.
func BentCigar ¶
BentCigar is the Bent Cigar benchmark function: unimodal and severely ill-conditioned. Typical bounds: [-100, 100].
func BetterConstrainedCandidate ¶
func BetterConstrainedCandidate(candidate, incumbent CandidateEvaluation, config *ConstraintConfig) bool
BetterConstrainedCandidate reports whether candidate is preferred over incumbent under config. A nil config falls back to ordinary objective minimization.
The default policy is Deb's three feasibility rules:
- a feasible candidate beats an infeasible one;
- between two infeasible candidates, the smaller violation wins;
- between two feasible candidates, the smaller cost wins.
Deb's rules need no penalty factor to tune, which is why they are the default. ConstraintHandlingPenalty instead ranks by penalized score and only consults the feasibility rules to break an exact tie, so that two candidates with the same score are still ordered by feasibility rather than arbitrarily.
func BinaryPositionsValid ¶
BinaryPositionsValid reports whether every component of position is exactly zero or one. It is the invariant a binary run maintains from initialization onwards, exposed so that a caller inspecting a Result or a population snapshot can assert it too.
func Discus ¶
Discus is the Discus benchmark function: unimodal and ill-conditioned along a single direction. Typical bounds: [-100, 100].
func DixonPrice ¶
DixonPrice is the Dixon-Price benchmark function: a valley-shaped, unimodal landscape. Typical bounds: [-10, 10].
func ExpandedSchafferF6 ¶
ExpandedSchafferF6 is the Expanded Schaffer F6 benchmark function: multimodal with concentric ripples. Typical bounds: [-100, 100].
func Griewank ¶
Griewank is the Griewank benchmark function: a product term creates many regularly spaced local minima. Global minimum is at f(0, ..., 0) = 0.
func HappyCat ¶
HappyCat is the HappyCat benchmark function: multimodal with a curved, thin optimal region. Typical bounds: [-2, 2].
func IsFeasible ¶
IsFeasible reports whether an aggregate constraint violation is zero.
func Levy ¶
Levy is the Levy benchmark function: multimodal with a strongly oscillating surface. Typical bounds: [-10, 10].
func ListPresets ¶
func ListPresets() map[ConfigPreset]string
ListPresets returns every available preset with a one-line description.
func ListVariants ¶
func ListVariants() []string
ListVariants returns the canonical variant names in the canonical order.
func LookupTransferFunction ¶
func LookupTransferFunction(name TransferFunction) (func(float64) float64, error)
LookupTransferFunction returns the named transfer function.
An unknown name is an error rather than a silent fallback to the default: a misspelled name in a JSON configuration would otherwise run a different algorithm than the one the caller wrote down.
func Michalewicz ¶
Michalewicz is the Michalewicz benchmark function: steep valleys and ridges controlled by a steepness parameter. Typical bounds: [0, pi].
func PenalizedCost ¶
func PenalizedCost(cost, violation, factor float64, method PenaltyMethod) float64
PenalizedCost folds an aggregate violation into a raw objective cost.
PenaltyLinear adds factor*violation; PenaltyQuadratic adds factor*violation². An empty method defaults to quadratic, which is the usual choice: it leaves small violations almost free and makes large ones prohibitive, so the swarm can cross a thin infeasible ridge without settling on the far side of a thick one.
func PresetNames ¶
func PresetNames() []string
PresetNames returns the known preset names in a stable alphabetical order.
func PrintPresets ¶
func PrintPresets()
PrintPresets writes every available preset and its description to standard output, for a command-line front end that offers a --list-presets flag.
func PrintRecommendations ¶
func PrintRecommendations(recommendations []AlgorithmRecommendation)
PrintRecommendations writes a ranked recommendation table to standard output.
func Rastrigin ¶
Rastrigin is the Rastrigin benchmark function: highly multimodal with a regular lattice of local minima. Global minimum is at f(0, ..., 0) = 0.
func Rosenbrock ¶
Rosenbrock is the Rosenbrock benchmark function (the "banana" function): a narrow, curved valley. Global minimum is at f(1, ..., 1) = 0.
func SaveConfig ¶
SaveConfig writes a Config to a JSON file, creating or truncating it.
ObjectiveFunc, Rand and the constraint function slices are not written -- they cannot be serialized -- so a file written here always needs its ObjectiveFunc restored in code after LoadConfig. Every serializable field is emitted, including a weight left at WeightAuto and a weight deliberately pinned to zero, which are distinct settings and stay distinct on the way back in.
func SchafferN1 ¶
SchafferN1 is the Schaffer N.1 benchmark problem: the classic one-variable, two-objective problem, with a convex front produced by two shifted parabolas pulling in opposite directions. The front is the image of x in [0, 2]; only the first component of the position is read. Typical bounds: [-10, 10], though the original problem allows anything up to [-10^5, 10^5].
func Schwefel ¶
Schwefel is the Schwefel benchmark function: deceptive, with the global minimum far from the next best local minima. Typical bounds: [-500, 500].
func Sphere ¶
Sphere is the Sphere benchmark function: a smooth, convex, unimodal bowl. Global minimum is at f(0, ..., 0) = 0.
func ValidateConfig ¶
ValidateConfig checks a configuration and reports the first problem it finds as an error naming the offending field by its JSON name.
It is the exported face of the same checks Optimize runs, so a caller can fail fast -- when reading a file, when accepting configuration from a user, or in a test -- instead of discovering the problem at the start of a run. A configuration that passes here is accepted by Optimize.
func VariantAliases ¶
func VariantAliases() []string
VariantAliases returns every accepted name, including aliases, in a stable alphabetical order. It is what an error message or a --help text lists.
func Weierstrass ¶
Weierstrass is the Weierstrass benchmark function: continuous everywhere but differentiable nowhere. Typical bounds: [-0.5, 0.5].
func ZDT1 ¶
ZDT1 is the ZDT1 benchmark problem: two objectives over a convex, continuous Pareto front. The front is f2 = 1 - sqrt(f1) for f1 in [0, 1], reached when x[1:] are all zero. Typical bounds: [0, 1] in every dimension, with 30 dimensions in the original suite.
func ZDT2 ¶
ZDT2 is the ZDT2 benchmark problem: ZDT1's landscape with a concave Pareto front. The front is f2 = 1 - f1² for f1 in [0, 1], reached when x[1:] are all zero. Typical bounds: [0, 1] in every dimension, with 30 dimensions in the original suite.
func ZDT3 ¶
ZDT3 is the ZDT3 benchmark problem: a Pareto front broken into five disconnected pieces. The front is f2 = 1 - sqrt(f1) - f1·sin(10πf1) for f1 in [0, 1], reached when x[1:] are all zero; the sine term is what disconnects it, and what makes ZDT3 the problem an archive that collapses onto one region fails first. Typical bounds: [0, 1] in every dimension, with 30 dimensions in the original suite.
Types ¶
type AlgorithmRecommendation ¶
type AlgorithmRecommendation struct {
// Variant is the recommended variant.
Variant AlgorithmVariant
// Reason explains, in one line, why this variant scored as it did.
Reason string
// Preset names the configuration factory to start from. It reflects the
// problem's shape, not the variant, so every recommendation for a given
// problem carries the same preset -- except that a discrete problem always
// gets PresetBinary.
Preset ConfigPreset
// Score is the fit in [0,1], from AlgorithmVariant.ApplicableTo.
Score float64
// Confidence is how much weight to put on Score, in [0,1]. It is lower
// where the heuristics are guessing: a variant scored outside the problem
// class it was written for, or a high-overhead variant on an expensive
// objective.
Confidence float64
}
AlgorithmRecommendation is one variant scored against a problem, with the reason it was scored that way.
Reason is never empty. A recommendation a caller cannot interrogate is worth no more than a coin flip, and this layer is heuristic enough that the caller deserves to see the heuristic.
func RecommendForBenchmark ¶
func RecommendForBenchmark(benchmarkName string) AlgorithmRecommendation
RecommendForBenchmark recommends a variant for a named benchmark function from functions.go.
An unrecognized name falls back to a generic 30-dimensional multimodal continuous problem -- the shape most benchmark suites are dominated by -- and says so in the Reason, so a typo does not look like a considered answer.
type AlgorithmSelector ¶
type AlgorithmSelector struct {
// contains filtered or unexported fields
}
AlgorithmSelector ranks the available variants against a problem.
func NewAlgorithmSelector ¶
func NewAlgorithmSelector() *AlgorithmSelector
NewAlgorithmSelector creates a selector over every variant, in the canonical order.
func NewAlgorithmSelectorFor ¶
func NewAlgorithmSelectorFor(variants ...AlgorithmVariant) *AlgorithmSelector
NewAlgorithmSelectorFor creates a selector over a given set of variants.
func (*AlgorithmSelector) RecommendAlgorithms ¶
func (s *AlgorithmSelector) RecommendAlgorithms( characteristics ProblemCharacteristics, ) []AlgorithmRecommendation
RecommendAlgorithms returns every variant scored against the problem, best first. Ties keep the canonical variant order, so the ranking is stable.
func (*AlgorithmSelector) RecommendBest ¶
func (s *AlgorithmSelector) RecommendBest( characteristics ProblemCharacteristics, ) AlgorithmRecommendation
RecommendBest returns the single best-scoring variant for the problem.
type AlgorithmStatistics ¶
type AlgorithmStatistics struct {
Mean float64 `json:"mean"`
Median float64 `json:"median"`
StdDev float64 `json:"stddev"`
Best float64 `json:"best"`
Worst float64 `json:"worst"`
// SuccessRate is the percentage of runs that reached the target cost. It is
// zero when no target was configured.
SuccessRate float64 `json:"success_rate"`
AvgFuncEvals float64 `json:"avg_function_evaluations"`
AvgTime float64 `json:"avg_execution_seconds"`
}
AlgorithmStatistics aggregates one variant's runs.
type AlgorithmVariant ¶
type AlgorithmVariant interface {
// Name returns the short canonical name of the variant: "DA", "BDA" or
// "MODA".
Name() string
// FullName returns the full descriptive name of the variant.
FullName() string
// Description returns a one-line summary of what the variant changes.
Description() string
// GetConfig returns a freshly allocated default configuration for this
// variant. You must still set ObjectiveFunc and ProblemSize, and for the
// continuous variants LowerBound and UpperBound.
GetConfig() *Config
// IsMultiObjective reports whether this variant optimizes several
// objectives at once, and therefore whether Run can honor its contract.
IsMultiObjective() bool
// Run executes the variant's single-objective entry point. A variant whose
// IsMultiObjective reports true returns ErrMultiObjectiveVariant.
Run(ctx context.Context, config *Config, options ...RunOption) (*Result, error)
// ApplicableTo scores how well this variant suits the given problem, in
// [0,1]. Higher is a better fit.
ApplicableTo(characteristics ProblemCharacteristics) float64
// EstimatedOverhead returns the approximate per-iteration cost relative to
// standard DA, as a multiplier. 1.0 is the baseline.
EstimatedOverhead() float64
// RecommendedFor returns the problem classes this variant excels at.
RecommendedFor() []string
}
AlgorithmVariant represents one variant of the Dragonfly Algorithm, so that the selector, the comparison runner and the builder can work with all three through one type.
Why Run returns *Result even though MODA cannot produce one ¶
MODA takes a MultiObjectiveConfig and returns a MultiObjectiveResult: an archive approximating a Pareto front, with no single incumbent. Three shapes were available for that. Widening Run's return type to an interface or `any` would push a type assertion onto every caller of every variant -- including ComparisonRunner, which computes means, medians and rank statistics over a scalar cost and is single-objective by construction. Splitting the interface in two would stop GetAllVariants from returning one slice, which the stable canonical order exists to provide. So Run keeps the single-objective signature, IsMultiObjective advertises which contract a variant honors, and MODAVariant.Run returns ErrMultiObjectiveVariant rather than a result it cannot honestly fill in. The multi-objective path is a separate method on the concrete type, mirroring OptimizeMultiObjective being a separate entry point.
func GetAllVariants ¶
func GetAllVariants() []AlgorithmVariant
GetAllVariants returns one fresh instance of every variant, in the canonical order given by ListVariants. The order is stable across calls and across processes; a test pins it.
func NewVariant ¶
func NewVariant(name string) (AlgorithmVariant, error)
NewVariant creates an algorithm variant by name, case-insensitively and ignoring surrounding space.
Recognized names:
- "da" or "standard" -- the continuous Dragonfly Algorithm
- "bda" or "binary" -- the binary Dragonfly Algorithm
- "moda" -- the multi-objective Dragonfly Algorithm
An unknown name is an error rather than a nil variant: a caller that gets a name from a flag or a configuration file should hear about the typo here, not through a nil dereference several calls later.
func SingleObjectiveVariants ¶
func SingleObjectiveVariants() []AlgorithmVariant
SingleObjectiveVariants returns the variants whose Run can honor its contract, in the canonical order. It is what ComparisonRunner defaults to, because the comparison statistics are all defined over a scalar cost.
type BDAVariant ¶
type BDAVariant struct{}
BDAVariant is the binary Dragonfly Algorithm: the same step vector as DA, turned into a per-bit flip probability by a transfer function.
func (*BDAVariant) ApplicableTo ¶
func (v *BDAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
ApplicableTo scores the binary variant against a problem.
func (*BDAVariant) Description ¶
func (v *BDAVariant) Description() string
Description returns a one-line summary.
func (*BDAVariant) EstimatedOverhead ¶
func (v *BDAVariant) EstimatedOverhead() float64
EstimatedOverhead returns 1.0: the bit-flip update replaces the continuous position update rather than adding to it.
func (*BDAVariant) FullName ¶
func (v *BDAVariant) FullName() string
FullName returns the descriptive name.
func (*BDAVariant) GetConfig ¶
func (v *BDAVariant) GetConfig() *Config
GetConfig returns NewBinaryConfig, which already carries the unit bounds, the v3 transfer function and UseBinary.
func (*BDAVariant) IsMultiObjective ¶
func (v *BDAVariant) IsMultiObjective() bool
IsMultiObjective reports false.
func (*BDAVariant) RecommendedFor ¶
func (v *BDAVariant) RecommendedFor() []string
RecommendedFor lists the problem classes BDA suits.
type BoundaryMethod ¶
type BoundaryMethod string
BoundaryMethod names the rule that returns an out-of-range position to the search space.
const ( // BoundaryWrap is the paper's rule: a component that leaves the box // reappears at the opposite bound and its step component is redrawn // uniformly from [0,1). Wrapping is genuinely part of the algorithm's // exploration behavior, so it is the default. BoundaryWrap BoundaryMethod = "wrap" // BoundaryClamp pins an out-of-range component to the bound it crossed and // leaves the step untouched. It is Mayfly's maxVec/minVec idiom, and the // least surprising choice for constrained problems. BoundaryClamp BoundaryMethod = "clamp" // BoundaryReflect mirrors an out-of-range component back into the box and // inverts the sign of its step component, as if the boundary were a wall. BoundaryReflect BoundaryMethod = "reflect" )
type CandidateEvaluation ¶
CandidateEvaluation carries the two numbers the ranking rules compare: the raw objective cost and the aggregate constraint violation.
The cost stays raw on purpose. A penalized score is derived on demand by PenalizedCost, so a Result still reports the cost the caller's objective actually returned rather than a number the constraint policy invented.
type ComparisonResult ¶
type ComparisonResult struct {
// FriedmanResult is nil when fewer than two variants were compared.
FriedmanResult *FriedmanTestResult `json:"friedman,omitempty"`
BenchmarkName string `json:"benchmark"`
AlgorithmNames []string `json:"algorithms"`
RunResults [][]RunResult `json:"runs"`
Statistics []AlgorithmStatistics `json:"statistics"`
// Rankings[i] is variant i's rank by mean cost, 1 being best.
Rankings []int `json:"rankings"`
// WilcoxonTests[i][j] is the pairwise test between variants i and j. The
// diagonal is left zero.
WilcoxonTests [][]WilcoxonResult `json:"wilcoxon"`
// BestAlgorithm indexes the rank-1 variant, or -1 when the comparison
// failed before producing statistics.
BestAlgorithm int `json:"best_algorithm"`
// BaseSeed is the seed run 0 used. Run k of every variant used
// BaseSeed + k, so the whole comparison is reproducible from this number.
BaseSeed int64 `json:"base_seed"`
}
ComparisonResult is the complete outcome of a comparison.
func (*ComparisonResult) ExportToCSV ¶
func (cr *ComparisonResult) ExportToCSV(path string) error
ExportToCSV writes one row per run, in variant then run order.
func (*ComparisonResult) ExportToJSON ¶
func (cr *ComparisonResult) ExportToJSON(path string) error
ExportToJSON writes the complete comparison result as an indented document.
func (*ComparisonResult) PrintComparisonResults ¶
func (cr *ComparisonResult) PrintComparisonResults()
PrintComparisonResults writes the formatted report to standard output.
func (*ComparisonResult) WriteComparisonResults ¶
func (cr *ComparisonResult) WriteComparisonResults(w io.Writer) error
WriteComparisonResults writes a formatted statistical report and a relative quality chart. A longer bar is a better (lower) finite mean cost.
type ComparisonRunner ¶
type ComparisonRunner struct {
// Variants defaults to SingleObjectiveVariants(). A multi-objective
// variant is rejected: every statistic here is defined over a scalar cost.
Variants []AlgorithmVariant
Runs int
MaxIterations int
// TargetCost is the success threshold used for SuccessRate and
// ConvergenceAt. Zero disables both.
TargetCost float64
// MaxWorkers bounds concurrent runs when Parallel is set. Zero means
// runtime.NumCPU().
MaxWorkers int
// Seed is the base seed run 0 uses.
Seed int64
Verbose bool
Parallel bool
}
ComparisonRunner runs several variants over the same problem and the same seeds, and reports whether their differences are significant.
Seeds are paired: run k of every variant is given the seed BaseSeed + k, so the variants face identical starting swarms and identical random streams and the differences that remain are the algorithms'. That pairing is also what the Wilcoxon and Friedman tests assume.
When Parallel is true the objective function must be safe for concurrent use. MaxWorkers bounds concurrent runs; Config.EnableParallel is an independent inner limit. Parallelism changes only the order runs complete in, never which seed a run gets, so a parallel comparison is bit-identical to a sequential one.
func NewComparisonRunner ¶
func NewComparisonRunner() *ComparisonRunner
NewComparisonRunner creates a runner over every single-objective variant, with the 30 runs conventional for statistical significance in the metaheuristics literature.
func (*ComparisonRunner) Compare ¶
func (cr *ComparisonRunner) Compare( benchmarkName string, fn ObjectiveFunction, problemSize int, lower, upper float64, ) *ComparisonResult
Compare runs every variant on the problem and returns the comparison. A failing run is recorded in its RunResult and does not stop the comparison; use CompareContext when a failure should abort.
func (*ComparisonRunner) CompareContext ¶
func (cr *ComparisonRunner) CompareContext( ctx context.Context, benchmarkName string, fn ObjectiveFunction, problemSize int, lower, upper float64, ) (*ComparisonResult, error)
CompareContext runs every variant with cancellation and explicit error reporting. It returns no partial aggregate when any run fails, so a caller cannot mistake a broken comparison for a completed one.
func (*ComparisonRunner) WithIterations ¶
func (cr *ComparisonRunner) WithIterations(iterations int) *ComparisonRunner
WithIterations sets the maximum iterations per run.
func (*ComparisonRunner) WithMaxWorkers ¶
func (cr *ComparisonRunner) WithMaxWorkers(workers int) *ComparisonRunner
WithMaxWorkers bounds concurrent runs. Zero means runtime.NumCPU().
func (*ComparisonRunner) WithParallel ¶
func (cr *ComparisonRunner) WithParallel(parallel bool) *ComparisonRunner
WithParallel enables or disables concurrent runs.
func (*ComparisonRunner) WithRuns ¶
func (cr *ComparisonRunner) WithRuns(runs int) *ComparisonRunner
WithRuns sets the number of runs per variant.
func (*ComparisonRunner) WithSeed ¶
func (cr *ComparisonRunner) WithSeed(seed int64) *ComparisonRunner
WithSeed sets the base seed the paired per-run seeds are derived from.
func (*ComparisonRunner) WithTarget ¶
func (cr *ComparisonRunner) WithTarget(target float64) *ComparisonRunner
WithTarget sets the success threshold.
func (*ComparisonRunner) WithVariantNames ¶
func (cr *ComparisonRunner) WithVariantNames(names ...string) *ComparisonRunner
WithVariantNames sets the variants to compare by name. An unrecognized name is an error, reported when Compare runs.
func (*ComparisonRunner) WithVariants ¶
func (cr *ComparisonRunner) WithVariants(variants ...AlgorithmVariant) *ComparisonRunner
WithVariants sets the variants to compare.
func (*ComparisonRunner) WithVerbose ¶
func (cr *ComparisonRunner) WithVerbose(verbose bool) *ComparisonRunner
WithVerbose enables per-run progress output.
type Config ¶
type Config struct {
ObjectiveFunc ObjectiveFunction `json:"-"`
Rand *rand.Rand `json:"-"`
Convergence *ConvergenceConfig `json:"convergence,omitempty"`
Constraints *ConstraintConfig `json:"constraints,omitempty"`
BoundaryMethod BoundaryMethod `json:"boundary_method"`
// TransferFunc names the transfer function the binary variant turns a step
// component into a bit-flip probability with. An empty value means
// DefaultTransferFunction, the paper's v3. It is ignored by the continuous
// entry points.
TransferFunc TransferFunction `json:"transfer_function,omitempty"`
LowerBound float64 `json:"lower_bound"`
UpperBound float64 `json:"upper_bound"`
// InertiaWeightStart and InertiaWeightEnd bracket the linearly decreasing
// inertia weight w = start - t*(start-end)/T.
InertiaWeightStart float64 `json:"inertia_weight_start"`
InertiaWeightEnd float64 `json:"inertia_weight_end"`
// The five swarming weights. At WeightAuto each follows the paper's
// schedule; any other finite value is used literally for the whole run.
SeparationWeight float64 `json:"separation_weight"`
AlignmentWeight float64 `json:"alignment_weight"`
CohesionWeight float64 `json:"cohesion_weight"`
FoodWeight float64 `json:"food_weight"`
EnemyWeight float64 `json:"enemy_weight"`
// RadiusInitialDivisor and RadiusGrowth shape the neighborhood radius
// r = (ub-lb)/divisor + (ub-lb)*(t/T)*growth.
RadiusInitialDivisor float64 `json:"radius_initial_divisor"`
RadiusGrowth float64 `json:"radius_growth"`
// MaxStepRatio sets the step clamp ΔX_max = MaxStepRatio*(ub-lb).
MaxStepRatio float64 `json:"max_step_ratio"`
// EnemyCutoffFraction is the fraction of the run after which the enemy
// weight is forced to zero. The paper uses three quarters.
EnemyCutoffFraction float64 `json:"enemy_cutoff_fraction"`
// LevyBeta and LevyScale parameterize Mantegna's algorithm.
LevyBeta float64 `json:"levy_beta"`
LevyScale float64 `json:"levy_scale"`
ProblemSize int `json:"problem_size"`
NPop int `json:"npop"`
MaxIterations int `json:"max_iterations"`
MaxWorkers int `json:"max_workers"`
// UseLevyWalk selects the paper's Lévy random walk for a dragonfly with no
// neighbors and no food in range. Disabling it keeps such a dragonfly
// still for that iteration.
UseLevyWalk bool `json:"use_levy_walk"`
EnableParallel bool `json:"enable_parallel"`
// UseBinary marks a configuration as belonging to the binary variant: 0/1
// positions, the bit-flip position update, and the transfer function in
// TransferFunc. It is what NewBinaryConfig sets and what the variant
// registry dispatches on; OptimizeBinary and OptimizeBinaryContext run the
// binary variant regardless of it, and Optimize ignores it.
UseBinary bool `json:"use_binary"`
}
Config holds the configuration parameters for the Dragonfly Algorithm.
You must set ObjectiveFunc, ProblemSize, LowerBound and UpperBound; every other field has a usable default from NewDefaultConfig.
When EnableParallel is true, ObjectiveFunc may be called concurrently with distinct position vectors and must be safe for concurrent use.
func LoadConfig ¶
LoadConfig reads a Config from a JSON file written by SaveConfig.
The loaded configuration is NOT runnable as it stands: ObjectiveFunc, Rand and the constraint function slices carry `json:"-"` because functions and random sources cannot be serialized. The caller must set ObjectiveFunc before calling Optimize, and may set Rand to reproduce a recorded seed. Everything else -- bounds, swarm size, weights, schedules, the convergence block and the constraint policy -- round-trips exactly.
The file is validated on load with everything except the ObjectiveFunc requirement, so a malformed or contradictory file fails here rather than at the start of a run.
Absent JSON fields decode as Go zero values, and zero is a legitimate pinned weight rather than a request for the adaptive schedule (see WeightAuto). Write configuration files with SaveConfig, which always emits every field, rather than hand-authoring a partial one.
func NewBinaryConfig ¶
func NewBinaryConfig() *Config
NewBinaryConfig creates a configuration for BDA, the binary variant, where a position is a bit string and the step is turned into a per-bit flip probability by a transfer function. You must set ObjectiveFunc and ProblemSize; the bounds are fixed by the variant and are already set.
The search box is the unit interval, because a position component is a bit and every schedule that scales with (ub-lb) is written for that box. Config.BoundaryMethod and Config.UseLevyWalk are ignored in binary mode -- see OptimizeBinaryContext for why neither has a meaning for a 0/1 vector.
The step clamp is widened from a tenth of the box to six times it. The transfer functions saturate by |Δx| ≈ 6, so clamping there is what makes the whole range of flip probabilities reachable; the continuous default of 0.1 would cap every flip probability at about a tenth and freeze the swarm. Treat the exact value as this implementation's choice rather than a quoted paper constant until it has been checked against BDA.m.
func NewDefaultConfig ¶
func NewDefaultConfig() *Config
NewDefaultConfig creates a default configuration for the standard Dragonfly Algorithm, with every weight left on its adaptive schedule. You must set ObjectiveFunc, ProblemSize, LowerBound, and UpperBound.
func NewFastConvergenceConfig ¶
func NewFastConvergenceConfig() *Config
NewFastConvergenceConfig creates a configuration for a short run on a cheap or well-behaved objective, where a good answer soon beats the best answer eventually. You must set ObjectiveFunc, ProblemSize, LowerBound, and UpperBound.
It shortens the run, shrinks the swarm, and grows the neighborhood radius faster so the swarm becomes a single flock -- and therefore exploits the food source -- earlier. A larger step clamp lets it get there in fewer iterations.
func NewHighDimensionalConfig ¶
func NewHighDimensionalConfig() *Config
NewHighDimensionalConfig creates a configuration tuned for problems with many dimensions, where the search space is too large for the default swarm to cover in the default number of iterations. You must set ObjectiveFunc, ProblemSize, LowerBound, and UpperBound.
It enlarges the swarm and lengthens the run, and it slows the radius growth so that neighborhoods stay local for longer -- in high dimensions a radius that reaches the whole box early makes every dragonfly a neighbor of every other and collapses the swarm onto the food source.
func NewPresetConfig ¶
func NewPresetConfig(preset ConfigPreset) (*Config, error)
NewPresetConfig builds a fresh configuration from a named preset.
Each call returns a newly allocated Config, so a caller may mutate the result freely; presets are never shared. You must still set ObjectiveFunc, ProblemSize, LowerBound and UpperBound, exactly as with the factory functions the presets name.
type ConfigPreset ¶
type ConfigPreset string
ConfigPreset names one of the configuration factories in config.go, so that a preset can be chosen from a string -- a command-line flag, an environment variable or a field in a larger configuration file.
const ( // PresetDefault is NewDefaultConfig: the paper's standard continuous DA. PresetDefault ConfigPreset = "default" // PresetHighDimensional is NewHighDimensionalConfig: a larger swarm, a // longer run and a slower-growing neighborhood radius. PresetHighDimensional ConfigPreset = "high-dimensional" // PresetFastConvergence is NewFastConvergenceConfig: a short run on a // cheap objective, converging early at the cost of exploration. PresetFastConvergence ConfigPreset = "fast-convergence" // PresetBinary is NewBinaryConfig: BDA on the unit interval with the // paper's default v3 transfer function. PresetBinary ConfigPreset = "binary" )
func RecommendPreset ¶
func RecommendPreset(characteristics ProblemCharacteristics) ConfigPreset
RecommendPreset names the configuration factory a problem of this shape should start from.
A discrete problem needs NewBinaryConfig regardless of anything else: it is the only preset whose bounds, step clamp and transfer function match a bit string. Otherwise dimensionality decides first, because a swarm too small to cover the space cannot be rescued by tuning, and a stated time budget decides second.
type ConstraintConfig ¶
type ConstraintConfig struct {
Handling ConstraintHandlingMethod `json:"handling,omitempty"`
PenaltyMethod PenaltyMethod `json:"penalty_method,omitempty"`
Inequalities []ConstraintFunction `json:"-"`
Equalities []ConstraintFunction `json:"-"`
PenaltyFactor float64 `json:"penalty_factor,omitempty"`
EqualityTolerance float64 `json:"equality_tolerance,omitempty"`
}
ConstraintConfig configures optional problem constraints. The function fields are not serialized; JSON round-trips carry the policy only.
type ConstraintEvaluation ¶
ConstraintEvaluation describes the aggregate constraint state of a position. A zero violation is feasible; any positive violation measures how far the position sits outside the feasible region.
func EvaluateConstraints ¶
func EvaluateConstraints(position []float64, config *ConstraintConfig) ConstraintEvaluation
EvaluateConstraints evaluates and aggregates every configured constraint.
Inequality constraints contribute max(0, g(x)); equality constraints contribute max(0, |h(x)| - tolerance). A nil config means the problem is unconstrained and is reported feasible without calling anything.
A nil constraint function or a non-finite constraint value produces an infinite violation rather than an error: an unusable constraint has to lose every comparison, and infinity is the only violation that reliably does.
type ConstraintFunction ¶
ConstraintFunction evaluates a constraint at a position. Inequality constraints are satisfied when the returned value is less than or equal to zero. Equality constraints are satisfied when the absolute returned value is within the configured equality tolerance.
type ConstraintHandlingMethod ¶
type ConstraintHandlingMethod string
ConstraintHandlingMethod selects how constrained candidates are ranked against one another.
const ( // ConstraintHandlingFeasibility applies Deb's feasibility rules: a feasible // candidate always beats an infeasible one, two feasible candidates are // ranked by cost, and two infeasible ones by aggregate violation. ConstraintHandlingFeasibility ConstraintHandlingMethod = "feasibility" // ConstraintHandlingPenalty ranks candidates by their penalized cost. ConstraintHandlingPenalty ConstraintHandlingMethod = "penalty" )
type ConvergenceConfig ¶
type ConvergenceConfig struct {
// TargetCost stops the run when the best cost is less than or equal to the
// pointed-to value. A pointer distinguishes a disabled target from a target
// of zero.
TargetCost *float64 `json:"target_cost,omitempty"`
// MinImprovement is the absolute cost, penalty score, or constraint-
// violation reduction required to reset the stagnation counter. It must be
// non-negative.
MinImprovement float64 `json:"min_improvement"`
// StagnationIterations stops the run after this many consecutive iterations
// without a sufficient improvement. Zero disables stagnation detection.
StagnationIterations int `json:"stagnation_iterations"`
// MinIterations is the minimum number of iterations completed before either
// stopping criterion can terminate the run. Zero behaves as one because
// convergence is checked at iteration boundaries.
MinIterations int `json:"min_iterations"`
}
ConvergenceConfig controls optional early termination. MaxIterations remains the hard upper bound; successful target or stagnation checks may shorten a run after MinIterations completed iterations.
type ConvergenceExport ¶
type ConvergenceExport struct {
TerminationReason TerminationReason `json:"termination_reason,omitempty"`
BestPosition []float64 `json:"best_position,omitempty"`
WorstPosition []float64 `json:"worst_position,omitempty"`
Convergence []ConvergencePoint `json:"convergence"`
BestCost float64 `json:"best_cost"`
BestConstraintViolation float64 `json:"best_constraint_violation"`
WorstCost float64 `json:"worst_cost"`
WorstConstraintViolation float64 `json:"worst_constraint_violation"`
Seed int64 `json:"seed"`
FuncEvalCount int `json:"func_eval_count"`
IterationCount int `json:"iteration_count"`
}
ConvergenceExport is the document ExportConvergenceJSON writes: the convergence curve plus the run-level summary it belongs to.
The summary carries the enemy alongside the food source. The enemy is a property of the whole run rather than of any one iteration, so it has no column in the CSV export, but a reader of the JSON document wants both ends of the range the swarm searched.
type ConvergencePoint ¶
type ConvergencePoint struct {
Iteration int `json:"iteration"`
BestCost float64 `json:"best_cost"`
}
ConvergencePoint is one exported sample from a convergence curve. Iteration is one-based and BestCost is the best cost known after that iteration.
type DAVariant ¶
type DAVariant struct{}
DAVariant is the standard continuous Dragonfly Algorithm, the paper's single-objective variant and the baseline every other variant is measured against.
func (*DAVariant) ApplicableTo ¶
func (v *DAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
ApplicableTo scores the continuous variant against a problem.
func (*DAVariant) Description ¶
Description returns a one-line summary.
func (*DAVariant) EstimatedOverhead ¶
EstimatedOverhead returns the baseline, 1.0.
func (*DAVariant) IsMultiObjective ¶
IsMultiObjective reports false.
func (*DAVariant) RecommendedFor ¶
RecommendedFor lists the problem classes DA suits.
type Dragonfly ¶
type Dragonfly struct {
Position []float64
Step []float64
Cost float64
ConstraintViolation float64
}
Dragonfly represents a single dragonfly in the swarm.
Step is the paper's ΔX, the velocity analog: it is carried between iterations through the inertia weight, clamped to ±ΔX_max, and reset by the boundary handler and the Lévy branch.
type FriedmanTestResult ¶
type FriedmanTestResult struct {
ChiSquare float64 `json:"chi_square"`
PValue float64 `json:"p_value"`
DegreesOfFreedom int `json:"degrees_of_freedom"`
Significant bool `json:"significant"`
}
FriedmanTestResult is a Friedman test across every variant at once: the non-parametric analog of a repeated-measures ANOVA over the per-run ranks.
type Logger ¶
Logger receives structured optimization lifecycle events. *slog.Logger implements Logger. OptimizeContext invokes loggers synchronously on the calling goroutine.
type MODAVariant ¶
type MODAVariant struct{}
MODAVariant is the multi-objective Dragonfly Algorithm: DA's swarm mechanics with the food source and the enemy drawn from a hypercube-gridded Pareto archive.
It is the one variant whose Run cannot honor the AlgorithmVariant contract; see the note on the interface. Use GetMultiObjectiveConfig and RunMultiObjective.
func (*MODAVariant) ApplicableTo ¶
func (v *MODAVariant) ApplicableTo(characteristics ProblemCharacteristics) float64
ApplicableTo scores the multi-objective variant against a problem.
func (*MODAVariant) Description ¶
func (v *MODAVariant) Description() string
Description returns a one-line summary.
func (*MODAVariant) EstimatedOverhead ¶
func (v *MODAVariant) EstimatedOverhead() float64
EstimatedOverhead returns 1.2: the archive update, the grid rebuild and the two roulette draws are per-iteration work DA does not do.
func (*MODAVariant) FullName ¶
func (v *MODAVariant) FullName() string
FullName returns the descriptive name.
func (*MODAVariant) GetConfig ¶
func (v *MODAVariant) GetConfig() *Config
GetConfig returns the swarm block of NewMultiObjectiveConfig, so that a caller inspecting the variant's mechanics through the common interface sees the same schedules a MODA run uses. It is not on its own runnable as MODA; use GetMultiObjectiveConfig.
func (*MODAVariant) GetMultiObjectiveConfig ¶
func (v *MODAVariant) GetMultiObjectiveConfig() *MultiObjectiveConfig
GetMultiObjectiveConfig returns a freshly allocated default MODA configuration. You must still set ObjectiveFunc and Swarm's ProblemSize, LowerBound and UpperBound.
func (*MODAVariant) IsMultiObjective ¶
func (v *MODAVariant) IsMultiObjective() bool
IsMultiObjective reports true.
func (*MODAVariant) Name ¶
func (v *MODAVariant) Name() string
Name returns the canonical short name.
func (*MODAVariant) RecommendedFor ¶
func (v *MODAVariant) RecommendedFor() []string
RecommendedFor lists the problem classes MODA suits.
func (*MODAVariant) Run ¶
Run always returns ErrMultiObjectiveVariant. A MODA run has no single incumbent, so there is no honest *Result to return; call RunMultiObjective.
func (*MODAVariant) RunMultiObjective ¶
func (v *MODAVariant) RunMultiObjective( ctx context.Context, config *MultiObjectiveConfig, ) (*MultiObjectiveResult, error)
RunMultiObjective executes MODA through OptimizeMultiObjective.
type MultiObjectiveConfig ¶
type MultiObjectiveConfig struct {
ObjectiveFunc MultiObjectiveFunction `json:"-"`
Swarm *Config `json:"swarm"`
// Beta, Gamma and Delta are the hypercube roulette exponents. See the
// UNVERIFIED note on DefaultArchiveBeta before treating the defaults as
// paper values.
Beta float64 `json:"beta"`
Gamma float64 `json:"gamma"`
Delta float64 `json:"delta"`
ArchiveSize int `json:"archive_size"`
NGrid int `json:"n_grid"`
}
MultiObjectiveConfig configures a MODA run.
Swarm carries the shared mechanics -- bounds, population, iterations, weight schedules, boundary rule, Lévy parameters and the RNG -- so that everything the single-objective algorithm already documents keeps meaning the same thing here. Swarm.ObjectiveFunc is ignored: a multi-objective run scores positions through ObjectiveFunc below.
Use NewMultiObjectiveConfig to build one; you must then set ObjectiveFunc and Swarm's ProblemSize, LowerBound and UpperBound.
func NewMultiObjectiveConfig ¶
func NewMultiObjectiveConfig() *MultiObjectiveConfig
NewMultiObjectiveConfig creates a default MODA configuration. You must set ObjectiveFunc and Swarm's ProblemSize, LowerBound and UpperBound.
The archive parameters are the defaults documented -- and flagged as unverified -- on DefaultArchiveBeta.
type MultiObjectiveFunction ¶
MultiObjectiveFunction represents a multi-objective optimization function. It takes a position vector and returns one value per objective, all of them minimized. The returned slice must have the same length on every call.
type MultiObjectiveResult ¶
type MultiObjectiveResult struct {
// Only the iteration cap ends a MODA run today, so this is always
// TerminationMaxIterations; it is reported anyway so that a caller can read
// a MODA result the same way it reads a single-objective one.
TerminationReason TerminationReason
Archive *ParetoArchive
// ArchiveSizeCurve records the archive's size after each completed
// iteration, the multi-objective analog of Result.ConvergenceCurve. A
// curve that stops growing early is the usual sign of a run that has
// stagnated.
ArchiveSizeCurve []int
FuncEvalCount int
IterationCount int
Seed int64
}
MultiObjectiveResult holds the outcome of a MODA run.
There is no single best position to report, so Archive is the result: the approximation of the Pareto front the run converged on.
func OptimizeMultiObjective ¶
func OptimizeMultiObjective(ctx context.Context, config *MultiObjectiveConfig) (*MultiObjectiveResult, error)
OptimizeMultiObjective runs the multi-objective Dragonfly Algorithm, honoring context cancellation.
It is a separate entry point rather than a mode of OptimizeContext: a multi-objective run has no single best cost, so Result would have to report an incumbent that does not exist. Cancellation is checked at the top of every iteration and a canceled run returns a nil result and ctx.Err(), so a caller cannot mistake an aborted run for a completed one.
The swarm mechanics are identical to the single-objective algorithm. Only the food source and the enemy differ: both are drawn from the archive once per iteration, the food from a sparse hypercube and the enemy from a crowded one.
Example ¶
ExampleOptimizeMultiObjective shows MODA. There is no single best position to report, so the result is the archive: an approximation of the Pareto front, which is non-dominated by construction after every mutation.
package main
import (
"context"
"fmt"
"math/rand"
dragonfly "github.com/CWBudde/Dragonfly"
)
func main() {
config := dragonfly.NewMultiObjectiveConfig()
config.ObjectiveFunc = dragonfly.ZDT1
config.Swarm.ProblemSize = 10
config.Swarm.LowerBound = 0
config.Swarm.UpperBound = 1
config.Swarm.MaxIterations = 100
config.Swarm.NPop = 40
config.Swarm.Rand = rand.New(rand.NewSource(42))
result, err := dragonfly.OptimizeMultiObjective(context.Background(), config)
if err != nil {
panic(err)
}
fmt.Println(result.Archive.IsNonDominated(), result.IterationCount)
}
Output: true 100
func (*MultiObjectiveResult) ExportParetoCSV ¶
func (result *MultiObjectiveResult) ExportParetoCSV(path string) error
ExportParetoCSV writes the archived front to path, one row per solution, with an index column, one column per objective and one per decision variable.
The column count follows the archive's contents, so an empty archive yields a header-only file with just the index column rather than an error.
func (*MultiObjectiveResult) ExportParetoJSON ¶
func (result *MultiObjectiveResult) ExportParetoJSON(path string) error
ExportParetoJSON writes the archived front and the run summary to path as an indented ParetoExport document.
type ObjectiveFunction ¶
ObjectiveFunction represents a function to be optimized. It takes a read-only position vector and returns a fitness cost.
type ParetoArchive ¶
type ParetoArchive struct {
// Solutions is the archive contents, in insertion order. Every member is
// mutually non-dominated with every other; that invariant is restored by
// each mutation, not merely at the end of a run.
Solutions []*ParetoSolution
// Beta, Gamma and Delta are the roulette exponents; see the UNVERIFIED note
// on DefaultArchiveBeta.
Beta float64
Gamma float64
Delta float64
// MaxSize is the capacity. A successful insert past capacity evicts a member
// of the most crowded hypercube, so the archive never exceeds it.
MaxSize int
// NGrid is the number of hypercubes per objective.
NGrid int
// contains filtered or unexported fields
}
ParetoArchive holds a mutually non-dominated set of solutions, partitioned into a hypercube grid over objective space.
The grid is what turns "keep the non-dominated set" into "keep a non-dominated set that is spread out": objective space is divided into NGrid equal bins per objective, and every selection and deletion decision is a roulette draw over the occupied cells, weighted by how crowded each one is.
The archive is not safe for concurrent use.
func NewParetoArchive ¶
func NewParetoArchive(maxSize int) *ParetoArchive
NewParetoArchive creates an archive of the given capacity with the default grid parameters. A non-positive capacity falls back to DefaultArchiveSize.
func NewParetoArchiveWithGrid ¶
func NewParetoArchiveWithGrid(maxSize, nGrid int, beta, gamma, delta float64) *ParetoArchive
NewParetoArchiveWithGrid creates an archive with explicit grid parameters.
A non-positive maxSize or nGrid falls back to the corresponding default, and a negative exponent is raised to zero -- a negative exponent inverts the preference the roulette draw exists to express, which is never what a caller means.
func (*ParetoArchive) Add ¶
func (pa *ParetoArchive) Add(solution *ParetoSolution, rng *rand.Rand) bool
Add offers a solution to the archive and reports whether it was accepted.
The candidate is rejected when an archived solution dominates it or already occupies its exact objective vector; otherwise every solution the candidate dominates is removed and the candidate is appended. An insert that overflows MaxSize evicts one member of the most crowded hypercube, chosen by a roulette draw weighted N^Delta, so the archive never exceeds its capacity.
The archive stores a deep copy, so the caller may reuse the candidate.
rng is the last parameter by the package convention and is used only for the overflow eviction. A nil rng makes that eviction deterministic (the first member of the most crowded cell), which keeps the archive usable outside a seeded run.
func (*ParetoArchive) IsNonDominated ¶
func (pa *ParetoArchive) IsNonDominated() bool
IsNonDominated reports whether every archived solution is mutually non-dominated. It is the archive's central invariant, and it is cheap enough (O(n²·m)) that tests assert it after every mutation rather than once at the end of a run.
func (*ParetoArchive) Len ¶
func (pa *ParetoArchive) Len() int
Len reports the number of archived solutions.
func (*ParetoArchive) UpdateFromPopulation ¶
func (pa *ParetoArchive) UpdateFromPopulation(population []*ParetoSolution, rng *rand.Rand) int
UpdateFromPopulation offers every member of a population to the archive and returns how many were accepted.
Candidates are offered in slice order, so a seeded run is reproducible: the archive's contents depend on the order of the offers, not only on the set.
type ParetoExport ¶
type ParetoExport struct {
TerminationReason TerminationReason `json:"termination_reason,omitempty"`
Front []ParetoPoint `json:"front"`
ArchiveSizeCurve []int `json:"archive_size_curve,omitempty"`
Seed int64 `json:"seed"`
ArchiveSize int `json:"archive_size"`
FuncEvalCount int `json:"func_eval_count"`
IterationCount int `json:"iteration_count"`
}
ParetoExport is the document ExportParetoJSON writes: the archived front plus the run-level summary it belongs to.
type ParetoPoint ¶
type ParetoPoint struct {
Position []float64 `json:"position"`
Objectives []float64 `json:"objectives"`
Index int `json:"index"`
}
ParetoPoint is one exported archive member: its objective vector and the position that produced it.
type ParetoSolution ¶
type ParetoSolution struct {
Position []float64
ObjectiveValues []float64
DominatedSolutions []int
// GridIndex is the solution's hypercube coordinate, one component per
// objective, each in [0, NGrid-1].
GridIndex []int
CrowdingDistance float64
Rank int
DominationCount int
// GridKey flattens GridIndex into a single integer in base NGrid, so that
// occupancy can be counted with one map lookup rather than a slice compare.
GridKey int
}
ParetoSolution is one member of a Pareto archive: a position, the objective vector it scored, and the bookkeeping the sorting and grid helpers hang off it.
GridIndex and GridKey are MODA's addition to Mayfly's version of this type. They are maintained by the owning ParetoArchive and are only meaningful relative to that archive's current grid bounds, which move as the archive does: a solution's cell coordinates can change without the solution itself changing at all.
type PenaltyMethod ¶
type PenaltyMethod string
PenaltyMethod selects how aggregate constraint violation is folded into the objective cost.
const ( // PenaltyLinear adds factor * violation to the objective cost. PenaltyLinear PenaltyMethod = "linear" // PenaltyQuadratic adds factor * violation squared to the objective cost. PenaltyQuadratic PenaltyMethod = "quadratic" )
type PopulationObserver ¶
type PopulationObserver func(PopulationSnapshot)
PopulationObserver receives the whole swarm after each completed iteration. OptimizeContext invokes observers synchronously on the calling goroutine.
type PopulationSnapshot ¶
type PopulationSnapshot struct {
Swarm []Dragonfly
Best Best
Worst Best
Iteration int
EvaluationCount int
}
PopulationSnapshot is the state of the swarm after a completed iteration. Iteration is one-based. Every dragonfly, Best and Worst, is a deep copy: observers may retain or modify them without affecting the optimizer.
It is deliberately separate from Progress rather than an extension of it. Copying NPop position and step vectors once per iteration is not free, and the overwhelmingly common reason to observe a run is to watch the best cost fall, which Progress already answers. Callers who want the swarm itself -- to animate it, to measure diversity, to debug a variant's search behavior -- opt in with WithPopulationObserver and pay for it there.
Worst is the enemy the swarm is currently repelled from. It has no Mayfly counterpart and is carried here because every step of the algorithm is computed against it, so a snapshot without it cannot explain a move.
type ProblemCharacteristics ¶
type ProblemCharacteristics struct {
// Dimensionality is the number of decision variables.
Dimensionality int
// Modality describes how many optima the landscape has.
Modality Modality
// Landscape describes the terrain between them.
Landscape Landscape
// Discrete marks a search space whose variables are binary or otherwise
// discrete. It is what routes a problem to BDA.
Discrete bool
// ExpensiveEvaluations marks an objective whose evaluation dominates the
// run time, so that a variant's overhead matters.
ExpensiveEvaluations bool
// RequiresFastConvergence marks a run with a wall-clock or budget limit,
// where a good answer soon beats the best answer eventually.
RequiresFastConvergence bool
// RequiresStableConvergence marks a run whose variance across seeds
// matters as much as its mean.
RequiresStableConvergence bool
// MultiObjective marks a problem with several objectives to trade off. It
// is what routes a problem to MODA.
MultiObjective bool
}
ProblemCharacteristics describes an optimization problem well enough to pick a variant for it.
ClassifyProblem fills in Dimensionality, Modality, Landscape and RequiresStableConvergence by sampling the objective. Discrete, MultiObjective, ExpensiveEvaluations and RequiresFastConvergence are statements about the caller's problem and budget that no amount of sampling can recover, so the caller sets them.
func BenchmarkCharacteristics ¶
func BenchmarkCharacteristics(benchmarkName string) (ProblemCharacteristics, bool)
BenchmarkCharacteristics returns the hand-classified characteristics of a benchmark function from functions.go, and whether the name is known.
func ClassifyProblem ¶
func ClassifyProblem( fn ObjectiveFunction, size int, lower, upper float64, rng *rand.Rand, ) ProblemCharacteristics
ClassifyProblem samples an objective function to estimate its landscape.
It fills in Dimensionality, Modality, Landscape and RequiresStableConvergence. Discrete, MultiObjective, ExpensiveEvaluations and RequiresFastConvergence are left at false, because they are facts about the caller's problem and budget rather than about the function's values; set them on the returned value before passing it to a selector.
What it can and cannot see ¶
Modality and Landscape come from a handful of straight-line scans across the box: how often the function changes direction along a line, and how much total variation it accumulates relative to that line's own range. Both quantities are scale-free, so the same thresholds apply whatever the bounds and whatever the units of the cost.
Landscape is therefore only ever reported as Smooth or Rugged. Deceptive (gradients that lead away from the global optimum) and NarrowValley (an ill-conditioned basin) are statements about where the optimum is relative to the terrain, and a few dozen samples cannot establish either. A caller who knows their problem is Schwefel-like or Rosenbrock-like should say so by setting Landscape on the returned value.
The estimates are coarse heuristics. Treat a classification as a starting point a caller who knows their problem should override.
rng is the last parameter, as every stochastic helper in this package takes it. Pass a seeded generator to make a classification reproducible; nil draws a fresh one.
type Progress ¶
Progress describes the best solution known after a completed iteration. Iteration is one-based. Best and its Position are snapshots: observers may retain or modify them without affecting the optimizer.
type ProgressObserver ¶
type ProgressObserver func(Progress)
ProgressObserver receives a snapshot after each completed iteration. OptimizeContext invokes observers synchronously on the calling goroutine.
type Result ¶
type Result struct {
// ConvergenceCurve holds the best cost known at the end of each completed
// iteration, so it has IterationCount entries. It is non-increasing for
// unconstrained optimization; a constrained incumbent's raw cost may rise
// when feasibility or lower violation takes priority. Without early
// stopping, IterationCount equals MaxIterations. It is a history of costs,
// not a point in the search space.
//
// The solution itself is GlobalBest.Position.
ConvergenceCurve []float64
TerminationReason TerminationReason
GlobalBest Best
// Worst is the enemy: the worst position seen during the run, which the
// enemy term of every step is computed against. It is reported for
// inspection -- it is specific to this algorithm and has no counterpart in
// Mayfly's Result.
Worst Best
FuncEvalCount int
IterationCount int
Seed int64 // Random seed used for reproducibility
}
Result holds the results of the optimization.
func Optimize ¶
Optimize runs the Dragonfly Algorithm with a background context.
Example ¶
ExampleOptimize shows the four fields a run requires: the objective, the dimensionality and the two bounds. Everything else comes from the factory.
package main
import (
"fmt"
"math/rand"
dragonfly "github.com/CWBudde/Dragonfly"
)
func main() {
config := dragonfly.NewDefaultConfig()
config.ObjectiveFunc = dragonfly.Sphere
config.ProblemSize = 5
config.LowerBound = -10
config.UpperBound = 10
config.MaxIterations = 50
config.NPop = 20
// Seeding the generator makes the run reproducible. Leave Rand nil and
// OptimizeContext draws a seed and reports it in Result.Seed, so a run can
// still be reproduced after the fact.
config.Rand = rand.New(rand.NewSource(42))
result, err := dragonfly.Optimize(config)
if err != nil {
panic(err)
}
fmt.Println(len(result.GlobalBest.Position), result.IterationCount, result.TerminationReason)
}
Output: 5 50 maximum_iterations
func OptimizeBinary ¶
OptimizeBinary runs the binary Dragonfly Algorithm with a background context.
Start from NewBinaryConfig and set ObjectiveFunc and ProblemSize. The objective keeps the ordinary ObjectiveFunction signature and is handed a vector whose components are exactly 0 or 1, so the benchmark, constraint and monitoring machinery works unchanged.
Example ¶
ExampleOptimizeBinary shows BDA. Positions are bit strings, so the objective keeps the ordinary func([]float64) float64 signature and simply receives 0/1-valued input -- here the count of zero bits, which an all-ones string drives to zero.
package main
import (
"fmt"
"math/rand"
dragonfly "github.com/CWBudde/Dragonfly"
)
func main() {
config := dragonfly.NewBinaryConfig()
config.ObjectiveFunc = func(bits []float64) float64 {
zeros := 0.0
for _, bit := range bits {
zeros += 1 - bit
}
return zeros
}
config.ProblemSize = 20
config.MaxIterations = 200
config.NPop = 30
config.Rand = rand.New(rand.NewSource(42))
result, err := dragonfly.OptimizeBinary(config)
if err != nil {
panic(err)
}
fmt.Println(dragonfly.BinaryPositionsValid(result.GlobalBest.Position), result.GlobalBest.Cost)
}
Output: true 0
func OptimizeBinaryContext ¶
func OptimizeBinaryContext(ctx context.Context, config *Config, options ...RunOption) (*Result, error)
OptimizeBinaryContext runs the binary Dragonfly Algorithm, honoring context cancellation and the supplied run options.
BDA differs from DA in the position update and nowhere else. ΔX is built by the same five primitives, the same two-branch gating and the same clamp the continuous variant uses -- this function calls straight into dragonfly.go's step builders -- and the continuous position update is then discarded in favor of
x_j <- ¬x_j if rand < T(Δx_j) else x_j
where T is Config.TransferFunc, defaulting to the paper's v3.
Boundary handling ¶
Config.BoundaryMethod is ignored in binary mode. A 0/1 vector cannot leave [0,1], so there is nothing for a wrap, clamp or reflect rule to repair, and applying one anyway would be worse than useless: the wrap rule's Δx reset would silently overwrite the step the very next bit-flip decision is made from. The field is left alone rather than validated away, so that one Config can be handed to both entry points.
The Lévy walk has no binary counterpart either: it is a multiplicative displacement of a real-valued position. The food-out-of-range branch is therefore the local-swarming step for every dragonfly, isolated or not. swarm.go's documented empty-neighborhood fallbacks (A_i is the dragonfly's own step, C_i and S_i are zero) reduce it for an isolated dragonfly to a decaying carry of ΔX, which the transfer function reads as a diminishing random flip probability -- the exploration role the Lévy walk plays in the continuous variant. Config.UseLevyWalk is ignored.
func OptimizeContext ¶
OptimizeContext runs the Dragonfly Algorithm, honoring context cancellation and the supplied run options.
Cancellation is checked at the top of every iteration. A canceled run returns a nil result and ctx.Err(); partial results are deliberately not reported, so a caller cannot mistake an aborted run for a completed one.
Observers registered through WithProgressObserver and WithPopulationObserver receive deep copies and run synchronously on this goroutine. They must not draw random numbers or mutate what they are handed: a seeded run is required to be reproducible, and an observer that reaches back into the swarm would be a back door around that.
Example ¶
ExampleOptimizeContext shows the run lifecycle: a cancellable context, a structured logger, and an observer that is called once per completed iteration on the calling goroutine.
package main
import (
"context"
"fmt"
"io"
"log/slog"
"math/rand"
dragonfly "github.com/CWBudde/Dragonfly"
)
func main() {
config := dragonfly.NewDefaultConfig()
config.ObjectiveFunc = dragonfly.Rastrigin
config.ProblemSize = 4
config.LowerBound = -5.12
config.UpperBound = 5.12
config.MaxIterations = 25
config.NPop = 20
config.Rand = rand.New(rand.NewSource(42))
logger := slog.New(slog.NewJSONHandler(io.Discard, nil))
updates := 0
result, err := dragonfly.OptimizeContext(
context.Background(),
config,
dragonfly.WithLogger(logger),
dragonfly.WithProgressObserver(func(dragonfly.Progress) {
updates++
}),
)
if err != nil {
panic(err)
}
fmt.Println(result.IterationCount, updates, len(result.ConvergenceCurve))
}
Output: 25 25 25
func (*Result) ExportConvergenceCSV ¶
ExportConvergenceCSV writes the convergence curve to path as iteration and best_cost columns, with one row per completed iteration. An empty curve yields a header-only file rather than an error.
The enemy, Result.Worst, is a single run-level value and has no per-iteration column here; ExportConvergenceJSON reports it.
func (*Result) ExportConvergenceJSON ¶
ExportConvergenceJSON writes the convergence curve and the run summary to path as an indented ConvergenceExport document.
type RunOption ¶
type RunOption struct {
// contains filtered or unexported fields
}
RunOption customizes one optimization run. Its fields are intentionally private; construct options with WithInitialPopulation, WithProgressObserver, WithPopulationObserver and WithLogger.
func WithInitialPopulation ¶
WithInitialPopulation seeds the start of the swarm. The argument may contain fewer positions than the configured population; unfilled slots are initialized randomly. The positions are copied when this function is called and again when applied to a run.
func WithLogger ¶
WithLogger registers a structured logger for run lifecycle events. Passing nil disables logging. The logger receives optimization_started, iteration_completed, and optimization_completed events.
func WithPopulationObserver ¶
func WithPopulationObserver(observer PopulationObserver) RunOption
WithPopulationObserver registers an observer for the swarm. It is called once per completed iteration, after WithProgressObserver's observer. Passing a nil observer disables population reporting, which is the default: no copying happens unless an observer is registered.
func WithProgressObserver ¶
func WithProgressObserver(observer ProgressObserver) RunOption
WithProgressObserver registers an observer for iteration progress. Passing a nil observer disables progress reporting.
type RunResult ¶
type RunResult struct {
// Error is the run's error message, empty when the run succeeded. It is a
// string rather than an error so the result document serializes.
Error string `json:"error,omitempty"`
BestCost float64 `json:"best_cost"`
ExecutionTime float64 `json:"execution_seconds"`
Seed int64 `json:"seed"`
FuncEvals int `json:"function_evaluations"`
Iterations int `json:"iterations"`
// ConvergenceAt is the one-based iteration at which the run first reached
// ComparisonRunner.TargetCost, or zero if it never did or no target was set.
ConvergenceAt int `json:"convergence_at"`
}
RunResult is the outcome of one optimization run inside a comparison.
type TerminationReason ¶
type TerminationReason string
TerminationReason describes why an optimization run ended.
const ( // TerminationMaxIterations means the configured iteration cap was reached. TerminationMaxIterations TerminationReason = "maximum_iterations" // TerminationTargetCost means the configured target cost was reached. TerminationTargetCost TerminationReason = "target_cost" // TerminationStagnation means the best cost did not improve sufficiently // within the configured stagnation window. TerminationStagnation TerminationReason = "stagnation" )
type TransferFunction ¶
type TransferFunction string
TransferFunction names one of the standard transfer functions that turn a step component into a bit-flip probability.
The V-shaped family (v1..v4) is symmetric about zero: it reads the magnitude of a step as "how unsettled is this bit", and flips regardless of the step's sign. The S-shaped family (s1..s4) is monotone increasing: a positive step pushes the bit towards one and a negative step towards zero, in the sense that the flip probability crosses one half at zero.
const ( // TransferV1 is |erf(√π/2 · Δx)|. TransferV1 TransferFunction = "v1" // TransferV2 is |tanh(Δx)|. TransferV2 TransferFunction = "v2" // TransferV3 is |Δx / √(Δx²+1)|, the paper's default. TransferV3 TransferFunction = "v3" // TransferV4 is |(2/π)·arctan((π/2)·Δx)|. TransferV4 TransferFunction = "v4" // TransferS1 is 1/(1+e^(-2Δx)). TransferS1 TransferFunction = "s1" // TransferS2 is 1/(1+e^(-Δx)), the logistic sigmoid. TransferS2 TransferFunction = "s2" // TransferS3 is 1/(1+e^(-Δx/2)). TransferS3 TransferFunction = "s3" // TransferS4 is 1/(1+e^(-Δx/3)). TransferS4 TransferFunction = "s4" )
func TransferFunctionNames ¶
func TransferFunctionNames() []TransferFunction
TransferFunctionNames returns every registered transfer function in a stable order: v1..v4 then s1..s4.
type VariantBuilder ¶
type VariantBuilder struct {
// contains filtered or unexported fields
}
VariantBuilder is a fluent front end for configuring and running a variant.
It carries only the single-objective Config: the multi-objective path takes a different configuration type and a different entry point, so building one through the same chain would mean half the methods silently doing nothing. Use MODAVariant.GetMultiObjectiveConfig for MODA.
func NewBuilder ¶
func NewBuilder(variantName string) *VariantBuilder
NewBuilder creates a builder for the named variant. An unknown name is recorded on the builder and surfaces from Build, so a chain can be written without an error check at every link.
Example:
config, err := NewBuilder("bda").ForProblem(fn, 20, 0, 1).WithIterations(500).Build()
Example ¶
ExampleNewBuilder shows the fluent front end. The chain records the first error it hits rather than returning one at every link, so it is checked once at the end.
package main
import (
"fmt"
"math/rand"
dragonfly "github.com/CWBudde/Dragonfly"
)
func main() {
result, err := dragonfly.NewBuilder("da").
ForProblem(dragonfly.Rastrigin, 4, -5.12, 5.12).
WithIterations(25).
WithPopulation(20).
WithConfig(func(config *dragonfly.Config) {
config.Rand = rand.New(rand.NewSource(42))
}).
Optimize()
if err != nil {
panic(err)
}
fmt.Println(len(result.GlobalBest.Position), result.IterationCount)
}
Output: 4 25
func NewBuilderFromVariant ¶
func NewBuilderFromVariant(variant AlgorithmVariant) *VariantBuilder
NewBuilderFromVariant creates a builder around an existing variant instance.
func (*VariantBuilder) Build ¶
func (b *VariantBuilder) Build() (*Config, error)
Build returns the configured Config, or the first error the chain recorded.
func (*VariantBuilder) ForProblem ¶
func (b *VariantBuilder) ForProblem(fn ObjectiveFunction, size int, lower, upper float64) *VariantBuilder
ForProblem sets the objective function, the dimensionality and the bounds.
The bounds of a binary variant are fixed at the unit interval and are left alone; pass any values.
func (*VariantBuilder) GetVariant ¶
func (b *VariantBuilder) GetVariant() AlgorithmVariant
GetVariant returns the variant the builder was created for, or nil if the name was not recognized.
func (*VariantBuilder) Optimize ¶
func (b *VariantBuilder) Optimize() (*Result, error)
Optimize builds the configuration and runs the variant with a background context.
func (*VariantBuilder) OptimizeContext ¶
func (b *VariantBuilder) OptimizeContext(ctx context.Context, options ...RunOption) (*Result, error)
OptimizeContext builds the configuration and runs the variant, honoring cancellation.
func (*VariantBuilder) WithConfig ¶
func (b *VariantBuilder) WithConfig(edit func(*Config)) *VariantBuilder
WithConfig applies an arbitrary edit to the configuration under construction.
func (*VariantBuilder) WithIterations ¶
func (b *VariantBuilder) WithIterations(iterations int) *VariantBuilder
WithIterations sets the maximum number of iterations.
func (*VariantBuilder) WithPopulation ¶
func (b *VariantBuilder) WithPopulation(size int) *VariantBuilder
WithPopulation sets the swarm size.
type WilcoxonResult ¶
type WilcoxonResult struct {
Algorithm1 string `json:"algorithm_1"`
Algorithm2 string `json:"algorithm_2"`
// Winner is the variant with the lower costs when the difference is
// significant, and wilcoxonTie otherwise.
Winner string `json:"winner"`
// WStatistic is min(W+, W-), the smaller of the positive and negative
// signed-rank sums.
WStatistic float64 `json:"w_statistic"`
// PValue is two-tailed, from the normal approximation to W. It is
// unreliable below roughly ten non-tied pairs, where an exact table is
// needed; the approximation is reported anyway rather than withheld,
// because it is the number a reader can check against a table.
PValue float64 `json:"p_value"`
Significant bool `json:"significant"`
}
WilcoxonResult is a Wilcoxon signed-rank test between two variants.
The test is paired: run k of both variants used the same seed, so the two samples are matched observations of the same starting conditions rather than independent draws. That is what makes the signed-rank test the right test and what makes it far more sensitive than an unpaired alternative on the small run counts a comparison can afford.