// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package optim import ( "strings" "testing" "time" "sourcedock.dev/petrbalvin/tensor/internal/core" ) // Callback-contract pins: a gradient callback that returns a shorter // array than the problem has dimensions used to index past it, and a // differential-evolution population below four has no way to draw // three distinct others. // TestLBFGSGradientLength pins the callback contract. func TestLBFGSGradientLength(t *testing.T) { f := func(p *core.Array) (float64, error) { x, y := p.FloatAt(0), p.FloatAt(1) return x*x + y*y, nil } short, err := core.FromFloats([]float64{1}, 1) if err != nil { t.Fatal(err) } grad := func(*core.Array) (*core.Array, error) { return short, nil } start, err := core.FromFloats([]float64{1, 1}, 2) if err != nil { t.Fatal(err) } if _, _, err := MinimiseLBFGS(f, grad, start, LBFGSOptions{}); err == nil { t.Fatal("expected an error for a gradient of the wrong length") } else if !strings.Contains(err.Error(), "gradient callback") { t.Fatalf("error = %v, want a callback-length refusal", err) } } // TestDifferentialEvolutionPopulationFloor pins the option contract: a // population below four has no way to draw three distinct others, and // the search used to spin in the picking loop for ever. func TestDifferentialEvolutionPopulationFloor(t *testing.T) { lo, err := core.FromFloats([]float64{-5}, 1) if err != nil { t.Fatal(err) } hi, err := core.FromFloats([]float64{5}, 1) if err != nil { t.Fatal(err) } f := func(p *core.Array) (float64, error) { v := p.FloatAt(0); return v * v, nil } done := make(chan error, 1) go func() { _, _, err := MinimiseDifferentialEvolution(f, lo, hi, DifferentialEvolutionOptions{Population: 3, Generations: 5}) done <- err }() select { case err := <-done: if err == nil { t.Fatal("expected an error for a population of 3") } else if !strings.Contains(err.Error(), "population") { t.Fatalf("error = %v, want a population refusal", err) } case <-time.After(10 * time.Second): t.Fatal("MinimiseDifferentialEvolution did not return: the picking loop spins") } // The floor itself still works. if _, _, err := MinimiseDifferentialEvolution(f, lo, hi, DifferentialEvolutionOptions{Population: 4, Generations: 20}); err != nil { t.Fatalf("population 4: %v", err) } }