# Erweiterte Anleitung: Hinzufügen einer neuen Testfunktion (mit Templates und Properties-Liste)

Basierend auf deinem Feedback habe ich die gestraffte Version weiter verfeinert: Templates (Code-Snippets) sind nun prominent platziert und als Kopierbare Blöcke hervorgehoben, um Konsistenz zu erzwingen. Die Liste der erlaubten Eigenschaften stammt direkt aus dem Readme.md (Abschnitt "Valid Properties") und src/NonlinearOptimizationTestFunctions.jl (VALID_PROPERTIES-Set). Ich habe sie als Tabelle integriert, mit Beschreibungen und Beispielen für Klarheit. Das reduziert Abweichungen (z. B. bei Signaturen) und macht die Anleitung handlungsstärker.

Ergänzung zu Properties: "non-scalable" ist keine zulässige Property (redundant: Fehlen von "scalable" impliziert fixed Dimension). "non-separable" ist zulässig, aber prüfe auf "fully non-separable" bei Bedarf (aus Literatur wie Jamil & Yang). Passe Implementierungen an: Verwende nur VALID_PROPERTIES; bei Verstoß (z. B. ArgumentError) erweitere VALID_PROPERTIES und Readme.

---

## Ziel
Beschreibe die Schritte zum Hinzufügen einer neuen Testfunktion (lowercase-Namen, z. B. biggsexp3.jl). Tests zentralisiert: Funktionsspezifisch in test/<functionname>_tests.jl; Gradienten/AD in test/runtests.jl; Optimierung in test/minima_tests.jl. Keine Redundanzen oder Debugging-Ausgaben.

Wichtiges Verbot: Keine explizite gradient!-Definition – auto-generiert als (G, x) -> copyto!(G, grad(x)).

Konstruktor-Template: TestFunction(f, grad, meta) (meta als Dict mit :name, :start, :min_position, :min_value, :properties, :lb, :ub). Keine benannten Args (MethodError).

Einbindung: include("test/<functionname>_tests.jl") in test/include_testfiles.jl. Test: julia --project=. -e 'using Pkg; Pkg.instantiate(); include("test/<functionname>_tests.jl")'.

## Recherche und Validierung
Recherchiere Minima/Werte/Schranken in Quellen (vermeide Rundungen). Validiere: tf.f(tf.meta[:min_position]()) ≈ tf.meta[:min_value]() atol=1e-6.

Quellen-Liste (einmalig):
- Al-Roomi (2015): https://www.al-roomi.org/benchmarks/unconstrained
- Gavana (2013): http://infinity77.net/global_optimization/test_functions.html
- Hedar (2005): http://www-optima.amp.i.kyoto-u.ac.jp/member/student/hedar/Hedar_files/TestGO.htm
- Jamil & Yang (2013): https://arxiv.org/abs/1308.4008
- Molga & Smutnicki (2005): http://www.zsd.ict.pwr.wroc.pl/files/docs/functions.pdf
- Suganthan et al. (2005): https://www.lri.fr/~hansen/Tech-Report-May-30-05.pdf
- Weitere: sfu.ca/~ssurjano, geatbx.com, indusmic.com.

Häufige Fehler (konsolidiert):
- Werte: Branin([0,0])=55.602; Bohachevsky2([1,1])=3.6; Bohachevsky3([0.01,0.01])≈0.0405.
- Minima: Six-Hump=-1.031628 bei (±0.0898,±0.7127).
- Schranken: BartelsConn=[-500,-500]~[500,500]; Bohachevsky2/3=[-100,-100]~[100,100].
- Multimodal: Alternativen in :description dokumentieren (z. B. Langermann).

## Erlaubte Eigenschaften (aus Readme.md)
Nur Properties aus diesem Set verwenden (VALID_PROPERTIES in src/). Neue? Ergänze Set und Readme ("Valid Properties"-Abschnitt). Füge "bounded" bei definierten Schranken hinzu; "scalable" nur bei variabler n (impliziert fixed ohne "fixed" – redundant, vermeiden). "non-scalable" ungültig (nicht in Set); "non-separable" zulässig, aber prüfe "fully non-separable" für starke Kopplung.

| Property                  | Beschreibung                                                                 | Beispiele (Funktionen)          |
|---------------------------|-----------------------------------------------------------------------------|---------------------------------|
| bounded                  | Hat definierte Schranken (:lb, :ub).                                        | Branin, Bohachevsky2            |
| continuous               | Kontinuierlich überall.                                                     | Ackley, Rosenbrock              |
| controversial            | Minimum umstritten (z. B. mehrere Quellen).                                 | Langermann                      |
| convex                   | Konvex (globales Minimum = einziges).                                       | Sphere                          |
| deceptive                | Täuscht lokale Optima vor.                                                  | -                               |
| differentiable           | Differenzierbar überall.                                                    | Rosenbrock                      |
| finite_at_inf            | Endlicher Wert bei ±∞.                                                      | -                               |
| fully non-separable      | Vollständig nicht-separabel.                                                | -                               |
| has_constraints          | Hat Nebenbedingungen.                                                       | -                               |
| has_noise                | Stochastisch/noisy.                                                         | -                               |
| highly multimodal        | Stark multimodal (viele Local Minima).                                      | Rastrigin                       |
| multimodal               | Mehrere Local Minima.                                                       | Branin                          |
| non-convex               | Nicht-konvex.                                                               | Ackley                          |
| non-separable            | Nicht-separabel (Variablen gekoppelt).                                      | Rosenbrock                      |
| partially differentiable | Teilweise differenzierbar.                                                  | BartelsConn                     |
| partially separable      | Teilweise separabel.                                                        | -                               |
| quasi-convex             | Quasi-konvex.                                                               | -                               |
| scalable                 | Skalierbar (beliebige n).                                                   | Ackley, Rastrigin               |
| separable                | Separabel (unabhängige Variablen).                                          | Sphere                          |
| strongly convex          | Stark konvex.                                                               | Quadratic                       |
| unimodal                 | Unimodal (ein Global Minimum).                                              | Sphere                          |

## Implementierung: Code-Templates
Template für src/functions/<name>.jl (kopiere und passe an; z. B. für nicht-skalierbar, n=3 wie Biggs EXP3):

# src/functions/<name>.jl
# Purpose: Implementation of the <Full Name> test function.
# Context: <Details: scalable/non-scalable, source, properties>.
# Global minimum: f(x*)=<value> at x*=<pos>.
# Bounds: <lb> ≤ x_i ≤ <ub>.
# Last modified: <Date>.

export <UPPERCASE>_FUNCTION, <name>, <name>_gradient

# Constants (if needed, e.g. for Biggs EXP3)
const <CONST_T> = 0.1 * collect(1:10)
const <CONST_Y> = exp.(-<CONST_T>) .- 5 * exp.(-10 * <CONST_T>)

function <name>(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n != <DIM> && throw(ArgumentError("<Name> requires exactly <DIM> dimensions"))
    
    # Extract vars: x1, x2, ... = x[1], x[2], ...
    sum_sq = zero(T)
    @inbounds for i in 1:<LOOP_SIZE>  # e.g. 10
        t_i = <CONST_T>[i]
        y_i = <CONST_Y>[i]
        e_i = exp(-t_i * x1) - x3 * exp(-t_i * x2) - y_i  # Adapt formula
        sum_sq += e_i^2
    end
    return sum_sq
end

function <name>_gradient(x::AbstractVector{T}) where {T<:Union{Real, ForwardDiff.Dual}}
    n = length(x)
    n == 0 && throw(ArgumentError("Input vector cannot be empty"))
    n != <DIM> && throw(ArgumentError("<Name> requires exactly <DIM> dimensions"))
    
    # Extract vars
    grad = zeros(T, <DIM>)
    @inbounds for i in 1:<LOOP_SIZE>
        # Compute partials: grad[1] += ... (adapt analytically)
    end
    return grad
end

const <UPPERCASE>_FUNCTION = TestFunction(
    <name>,
    <name>_gradient,
    Dict(
        :name => "<name>",
        :description => "<Beschreibung: Source, Eigenschaften, Multimodal-Notes>.",
        :math => raw"""f(\mathbf{x}) = <LaTeX-Formel>. """,
        :start => () -> [<start1>, <start2>, <start3>],  # ()-> for non-scalable
        :min_position => () -> [<pos1>, <pos2>, <pos3>],
        :min_value => () -> <value>,  # ()-> for non-scalable
        :properties => ["<prop1>", "<prop2>", ...],  # From table above
        :lb => () -> [<lb1>, <lb2>, <lb3>],
        :ub => () -> [<ub1>, <ub2>, <ub3>],

    )
)

Template für skalierbar (z. B. Ackley, n>=1): Ersetze () -> [...] durch (n::Int) -> (n < 1 && throw(...); fill(<val>, n)); füge "scalable" zu properties.

Für Matrizen (skalierbar): 
A = Diagonal(fill(2.0, n))  # Positiv definit; prüfe all(eigvals(A) .> 0)

Abhängigkeiten: In Project.toml: Random = "..."; dann Pkg.resolve(); Pkg.instantiate().

Top-Level-Check: using NonlinearOptimizationTestFunctions; @test isdefined(NonlinearOptimizationTestFunctions, :<name>).

Cache-Clear: Nach Änderungen: rmdir /s /q ~/.julia/compiled.

## Tests: Template für test/<name>_tests.jl
# test/<name>_tests.jl
using Test, NonlinearOptimizationTestFunctions

tf = <UPPERCASE>_FUNCTION

@test tf.meta[:name] == "<name>"
@test has_property(tf, "<prop1>")  # For each in :properties
@test !has_property(tf, "scalable")  # If non-scalable
@test length(tf.meta[:properties]) == <count>

@test_throws ArgumentError tf.f(Float64[])

start_point = tf.meta[:start]()  # () for non-scalable
@test start_point ≈ [<val1>, ...] atol=1e-6
f_start = tf.f(start_point)
@test f_start ≈ <computed_start_val> atol=1e-6

min_pos = tf.meta[:min_position]()
@test min_pos ≈ [<pos1>, ...] atol=1e-6
f_min = tf.f(min_pos)
@test f_min ≈ tf.meta[:min_value]() atol=1e-6

lb, ub = tf.meta[:lb](), tf.meta[:ub]()
@test lb ≈ [<lb1>, ...]
@test ub ≈ [<ub1>, ...]

# Extra point
test_pt = [<pt1>, <pt2>, <pt3>]
@test tf.f(test_pt) ≈ <computed_val> atol=1e-6

# Non-diff (if applicable): @test_throws DomainError <name>_gradient([0.0, 0.0])

Für skalierbar: tf.meta[:start](2) (n=2 default).

## Schritte-Checkliste
1. Template kopieren/anpassen (src/functions).
2. Recherche/Validierung (Minima, Werte, Properties aus Tabelle).
3. Test-Template (test/<name>_tests.jl), einbinden.
4. Gesamttests: julia --project=. test/runtests.jl.
5. Debug: @info "min_value: $(tf.meta[:min_value])" in minima_tests.jl bei MethodError.

Finale Validierung:
- [ ] Template-Signatur (Dispatch mit Dual).
- [ ] Properties aus Tabelle (kein "fixed", kein "non-scalable").
- [ ] :min_value konsistent (skalierbar: (n)->; nicht: ()).
- [ ] Tests passieren (standalone + runtests.jl).

Diese Version priorisiert Templates für Reproduzierbarkeit und integriert die Properties-Liste für schnelle Referenz. Für Biggs EXP4/5: Passe das Template an (n=4, ähnliche Formel). Testen?