Posted on Wednesday, August 19, 2009 10:14 PM
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Imager
{
public class Optimizer
{
List<Parameter> parameters = new List<Parameter>();
HashSet<Parameter> active = new HashSet<Parameter>();
int index;
public void RegisterParameter(double initial, double increment, bool optimize)
{
parameters.Add(new Parameter(initial, increment, optimize));
}
public void Run(Func<double[], double> lambda, Action<double[], double, int> bestSoFar)
{
int activeIndex;
{
Parameter p;
while (true)
{
if (index >= parameters.Count)
return; // no active parameters
p = parameters[index];
activeIndex = index;
if (p.Optimize) break;
index++;
}
active.Add(p);
}
var best = lambda(parameters.Select(x => x.Original).ToArray());
if (double.IsNaN(best))
return;
while (true)
{
var r = lambda(parameters.Select(x => active.Contains(x) ? x.Value : x.Original).ToArray());
if (double.IsNaN(r))
return;
var d = best - r;
foreach (var p in active)
p.ProcessResult(d);
if (d >= 0)
best = r;
if (d > 0)
bestSoFar(parameters.Select(x => x.Original).ToArray(), best, activeIndex);
else
{
active.Clear();
Parameter p;
while (true)
{
if (index == 0)
{
if (parameters.All(x=>x.Stagnated))
return; // no effect in optimizer
}
p = parameters[index];
activeIndex = index;
index++;
if (index >= parameters.Count)
index = 0;
if (p.Optimize) break;
}
active.Add(p);
}
}
}
private class Parameter
{
public double Value { get; private set; }
public double Increment { get; private set; }
public bool Optimize { get; private set; }
public double Original { get; private set; }
public bool Stagnated { get { return (!Optimize) || (reversals > 2); } }
private double magnitude;
private bool direction;
private int reversals = 0;
public Parameter(double value, double increment, bool optimize)
{
Increment = increment;
Optimize = optimize;
magnitude = Increment;
direction = true;
Original = value;
Value = value + magnitude;
}
public void ProcessResult(double delta)
{
if (!Optimize) return;
if (delta < 0.0d)
{
magnitude *= 0.5d;
if (direction ? (magnitude < Increment) : (magnitude > -Increment))
{
direction = !direction;
magnitude = direction ? Increment : -Increment;
reversals++;
}
Value = Original + magnitude;
}
else
{
if (delta > 0)
reversals = 0;
magnitude *= 2.0d;
Original = Value;
Value += magnitude;
}
}
}
}
}