using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MathNet.Numerics.LinearAlgebra; namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions { public abstract class BaseTestFunction : ITestFunction { public abstract string Description { get; } public abstract int ParameterDimension { get; } public abstract int ItemDimension { get; } public abstract double ItemValue(Vector x, int itemIndex); public abstract void ItemGradientByRef(Vector x, int itemIndex, Vector output); public abstract void ItemHessianByRef(Vector x, int itemIndex, Matrix output); public virtual Vector ItemGradient(Vector x, int itemIndex) { var output = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); this.ItemGradientByRef(x, itemIndex, output); return output; } public virtual Matrix ItemHessian(Vector x, int itemIndex) { var output = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); this.ItemHessianByRef(x, itemIndex, output); return output; } public virtual void JacobianbyRef(Vector x, Matrix output) { for (int ii = 0; ii < this.ItemDimension; ++ii) { var grad = this.ItemGradient(x, ii); output.SetRow(ii, grad); } } public virtual Matrix Jacobian(Vector x) { var output = new LinearAlgebra.Double.DenseMatrix(this.ItemDimension, this.ParameterDimension); this.JacobianbyRef(x, output); return output; } public virtual void SsqGradientByRef(Vector x, Vector output) { if (output.Count != this.ParameterDimension) throw new ArgumentException($"Output vector must match parameter dimension of function; expected {this.ParameterDimension}, got {output.Count}."); for (int jj = 0; jj < this.ParameterDimension; ++jj) output[jj] = 0.0; var tmp_grad = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); double tmp_value = 0.0; for (int ii = 0; ii < this.ItemDimension; ++ii) { tmp_value = this.ItemValue(x, ii); this.ItemGradientByRef(x, ii, tmp_grad); for (int jj = 0; jj < this.ParameterDimension; ++jj) output[jj] += 2 * tmp_value * tmp_grad[jj]; } } public virtual Vector SsqGradient(Vector x) { var output = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); this.SsqGradientByRef(x, output); return output; } public virtual void SsqHessianByRef(Vector x, Matrix output) { if (output.RowCount != this.ParameterDimension || output.ColumnCount != this.ParameterDimension) throw new ArgumentException($"Output matrix must match parameter dimension of function; expected {this.ParameterDimension}x{this.ParameterDimension}, got {output.RowCount}x{output.ColumnCount}."); for (int ii = 0; ii < this.ParameterDimension; ++ii) for (int jj = 0; jj < this.ParameterDimension; ++jj) output[ii,jj] = 0.0; var tmp_grad = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); var tmp_hess = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); double tmp_value = 0.0; for (int ii = 0; ii < this.ItemDimension; ++ii) { tmp_value = this.ItemValue(x, ii); this.ItemGradientByRef(x, ii, tmp_grad); this.ItemHessianByRef(x, ii, tmp_hess); for (int jj = 0; jj < this.ParameterDimension; ++jj) { for (int kk = 0; kk < this.ParameterDimension; ++kk) { var increment = 2 * (tmp_value * tmp_hess[jj, kk] + tmp_grad[jj] * tmp_grad[kk]); output[jj, kk] += increment; } } } } public virtual Matrix SsqHessian(Vector x) { var output = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); this.SsqHessianByRef(x, output); return output; } public virtual double SsqValue(Vector x) { double ssq = 0.0; for (int ii = 0; ii < this.ItemDimension; ++ii) { var tmp = this.ItemValue(x, ii); ssq += tmp * tmp; } return ssq; } } }