You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.1 KiB
166 lines
5.1 KiB
using System;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition.LTC
|
|
{
|
|
/// <summary>
|
|
/// Downhill simplex solver:
|
|
/// http://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method#One_possible_variation_of_the_NM_algorithm
|
|
/// Using the termination criterion from Numerical Recipes in C++ (3rd Ed.)
|
|
/// </summary>
|
|
internal class NelderMead
|
|
{
|
|
// standard coefficients from Nelder-Mead
|
|
const double reflect = 1.0;
|
|
const double expand = 2.0;
|
|
const double contract = 0.5;
|
|
const double shrink = 0.5;
|
|
|
|
int DIM;
|
|
int NB_POINTS;
|
|
double[][] s;
|
|
double[] f;
|
|
|
|
public int m_lastIterationsCount;
|
|
|
|
public delegate double ObjectiveFunctionDelegate(double[] _parameters);
|
|
|
|
public NelderMead(int _dimensions)
|
|
{
|
|
DIM = _dimensions;
|
|
NB_POINTS = _dimensions + 1;
|
|
s = new double[NB_POINTS][];
|
|
for (int i = 0; i < NB_POINTS; i++)
|
|
s[i] = new double[_dimensions];
|
|
f = new double[NB_POINTS];
|
|
}
|
|
|
|
public double FindFit(double[] _pmin, double[] _start, double _delta, double _tolerance, int _maxIterations, ObjectiveFunctionDelegate _objectiveFn)
|
|
{
|
|
// initialise simplex
|
|
Mov(s[0], _start);
|
|
for (int i = 1; i < NB_POINTS; i++)
|
|
{
|
|
Mov(s[i], _start);
|
|
s[i][i - 1] += _delta;
|
|
}
|
|
|
|
// evaluate function at each point on simplex
|
|
for (int i = 0; i < NB_POINTS; i++)
|
|
f[i] = _objectiveFn(s[i]);
|
|
|
|
double[] o = new double[DIM]; // Centroid
|
|
double[] r = new double[DIM]; // Reflection
|
|
double[] c = new double[DIM]; // Contraction
|
|
double[] e = new double[DIM]; // Expansion
|
|
|
|
int lo = 0, hi, nh;
|
|
for (m_lastIterationsCount = 0; m_lastIterationsCount < _maxIterations; m_lastIterationsCount++)
|
|
{
|
|
// find lowest, highest and next highest
|
|
lo = hi = nh = 0;
|
|
for (int i = 1; i < NB_POINTS; i++)
|
|
{
|
|
if (f[i] < f[lo])
|
|
lo = i;
|
|
if (f[i] > f[hi])
|
|
{
|
|
nh = hi;
|
|
hi = i;
|
|
}
|
|
else if (f[i] > f[nh])
|
|
nh = i;
|
|
}
|
|
|
|
// stop if we've reached the required tolerance level
|
|
double a = Math.Abs(f[lo]);
|
|
double b = Math.Abs(f[hi]);
|
|
if (2.0 * Math.Abs(a - b) < (a + b) * _tolerance)
|
|
break;
|
|
|
|
// compute centroid (excluding the worst point)
|
|
Set(o, 0.0f);
|
|
for (int i = 0; i < NB_POINTS; i++)
|
|
{
|
|
if (i == hi)
|
|
continue;
|
|
Add(o, s[i]);
|
|
}
|
|
|
|
for (int i = 0; i < DIM; i++)
|
|
o[i] /= DIM;
|
|
|
|
// reflection
|
|
for (int i = 0; i < DIM; i++)
|
|
r[i] = o[i] + reflect * (o[i] - s[hi][i]);
|
|
|
|
double fr = _objectiveFn(r);
|
|
if (fr < f[nh])
|
|
{
|
|
if (fr < f[lo])
|
|
{
|
|
// expansion
|
|
for (int i = 0; i < DIM; i++)
|
|
e[i] = o[i] + expand * (o[i] - s[hi][i]);
|
|
|
|
double fe = _objectiveFn(e);
|
|
if (fe < fr)
|
|
{
|
|
Mov(s[hi], e);
|
|
f[hi] = fe;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Mov(s[hi], r);
|
|
f[hi] = fr;
|
|
continue;
|
|
}
|
|
|
|
// contraction
|
|
for (int i = 0; i < DIM; i++)
|
|
c[i] = o[i] - contract * (o[i] - s[hi][i]);
|
|
|
|
double fc = _objectiveFn(c);
|
|
if (fc < f[hi])
|
|
{
|
|
Mov(s[hi], c);
|
|
f[hi] = fc;
|
|
continue;
|
|
}
|
|
|
|
// reduction
|
|
for (int k = 0; k < NB_POINTS; k++)
|
|
{
|
|
if (k == lo)
|
|
continue;
|
|
for (int i = 0; i < DIM; i++)
|
|
s[k][i] = s[lo][i] + shrink * (s[k][i] - s[lo][i]);
|
|
f[k] = _objectiveFn(s[k]);
|
|
}
|
|
}
|
|
|
|
// return best point and its value
|
|
Mov(_pmin, s[lo]);
|
|
return f[lo];
|
|
}
|
|
|
|
void Mov(double[] r, double[] v)
|
|
{
|
|
for (int i = 0; i < DIM; ++i)
|
|
r[i] = v[i];
|
|
}
|
|
|
|
void Set(double[] r, double v)
|
|
{
|
|
for (int i = 0; i < DIM; ++i)
|
|
r[i] = v;
|
|
}
|
|
|
|
void Add(double[] r, double[] v)
|
|
{
|
|
for (int i = 0; i < DIM; ++i)
|
|
r[i] += v[i];
|
|
}
|
|
}
|
|
}
|