using System;
using System.Diagnostics;
using Deltares.Mathematics;
// =======================================================================================================================
// Class name: TDLevenbergMarquardtExtreme
//
// Description:
//
// Copyright (c) 2010 Deltares
//
// Date ID Modification
// 2010-02-09 Sel Created
// 2010-02-10 Sel Improved LevenbergMarquardt algorithm code
// 2010-02-10 Sel Introduced MDVariationalCalculus
// 2010-02-15 Sel Allocated Bounds in LM; Replaced AllocateMemory by SetVariableCount.
// 2010-05-20 Sel Finetuned use of GA in general
namespace Deltares.Stability.Calculation2.Levenberg
{
// GeoLib units
public class LevenbergMarquardtExtreme : LevenbergMarquardt
{
// Private fields
private IExtremeCalculation levenbergMarquardtCalculation = null;
// Public properties
public IExtremeCalculation LevenbergMarquardtCalculation
{
get
{
return levenbergMarquardtCalculation;
}
set
{
levenbergMarquardtCalculation = value;
}
}
// =======================================================================================================================
// Date ID Modification
// 2010-02-09 Sel Created
// =======================================================================================================================
public override void SetVariableCount(int count)
{
base.SetVariableCount(count);
functionValues = new double[count];
}
// =======================================================================================================================
// Date ID Modification
// 2010-02-09 Sel Created
// =======================================================================================================================
public override void DetermineDrift(double[] parameters)
{
// Determine derivative with Drift and store result in vector
driftAfter = 0;
DetermineDerivative(parameters);
for (int variableIndex = 0; variableIndex < VariableCount; variableIndex ++)
{
driftAfter = driftAfter + Math.Pow(Convert.ToDouble(functionValues[variableIndex]), 2);
}
driftAfter = Math.Sqrt(driftAfter/VariableCount);
// The drift never will vanish. If so, ComputeExtremeResult failed and the result is improper and must be discarded.
if (driftAfter == 0)
{
driftAfter = 2*driftPrior;
}
}
// =======================================================================================================================
// Date ID Modification
// 2010-01-19 Sel Created
// =======================================================================================================================
public override void FillJacobian()
{
// This extreme determination is square
Debug.Assert(ParameterCount == VariableCount);
// Determine drift and copy derivative
DetermineDrift(parameters);
for (int variableIndex = 0; variableIndex < VariableCount; variableIndex ++)
{
jacobian.Vector[variableIndex] = functionValues[variableIndex];
}
// Determine variation of derivative and store result in matrix
for (int parameterIndex = 0; parameterIndex < ParameterCount; parameterIndex ++)
{
// Apply a small variation on the parameter concerned
double parameterValue = parameters[parameterIndex];
parameters[parameterIndex] = parameterValue + FShift;
// Determine derivative and store variation in matrix
DetermineDerivative(parameters);
for (int variableIndex = 0; variableIndex < VariableCount; variableIndex ++)
{
jacobian.Matrix[variableIndex][parameterIndex] = (functionValues[variableIndex] - jacobian.Vector[variableIndex])/FShift;
}
// Restore parameter
parameters[parameterIndex] = parameterValue;
}
// The numerical derivative is not precise. For a guess of the drift it is all right, but for the determination
// of the extreme a correction of half the shift times the second derivative will improve the outcome.
for (int variableIndex = 0; variableIndex < VariableCount; variableIndex ++)
{
jacobian.Vector[variableIndex] = jacobian.Vector[variableIndex] - FShift/2*jacobian.Matrix[variableIndex][variableIndex];
}
}
///
/// Determines the derivative
///
/// Parameters
/// Indication whether the underlying calculations were successful
private void DetermineDerivative(double[] parameters)
{
Debug.Assert((levenbergMarquardtCalculation != null));
// Determine function values and store the numeric derivative in vector
double functionValue = levenbergMarquardtCalculation.ComputeExtremeResult(parameters);
if (!IsValidResult(functionValue))
{
valid = false;
}
for (int parameterIndex = 0; parameterIndex < ParameterCount; parameterIndex ++)
{
// Apply a small variation, determine derivative and restore parameter
double parameterValue = parameters[parameterIndex];
parameters[parameterIndex] = parameterValue + FShift;
double shiftedResult = levenbergMarquardtCalculation.ComputeExtremeResult(parameters);
if (!IsValidResult(shiftedResult))
{
valid = false;
}
functionValues[parameterIndex] = (levenbergMarquardtCalculation.ComputeExtremeResult(parameters) - functionValue)/FShift;
parameters[parameterIndex] = parameterValue;
}
}
}
}