Pau*_*och 18 c# ms-solver-foundation numerical-integration
计划目的:整合.我正在实现高维度(高达100)的自适应正交(又称数值积分)算法.该想法是通过使用与该点处的误差估计成比例的采样密度来评估点来随机地将体积分成更小的部分.在早期我"老化"一个统一的样本,然后根据估计误差的高斯分布随机选择点.以类似于模拟退火的方式,I"降低温度"并随着时间的推移降低高斯的标准偏差,因此低误差点最初有很大的选择机会,但后来选择稳定下降可能性.这使程序能够偶然发现由于错误功能的缺陷而可能错过的尖峰.(我的算法在精神上类似于马尔可夫链蒙特卡罗积分.)
功能特征.要整合的功能是由于自然灾害导致的多个建筑物的保险政策损失估计.政策功能并不顺利:有免赔额,最高限额,分层数(例如,零赔偿支出高达100万美元,100%支付从1-2百万美元,然后零支付超过200万美元)和其他奇怪的政策条款.这引入了非线性行为和在许多平面中没有衍生物的函数.在政策功能之上是损坏功能,它根据建筑类型和飓风强度而变化,绝对不是钟形.
问题上下文:错误功能.困难在于选择一个好的错误功能.对于每个点,我记录对此有用的度量:函数的大小,由于先前的度量(一阶导数的代理)而改变了多少,该点占据的区域的体积(更大的体积可以更好地隐藏错误),以及与区域形状相关的几何因子.我的误差函数将是这些度量的线性组合,其中每个度量被赋予不同的权重.(如果我得到的结果很差,我会考虑非线性函数.)为了帮助我完成这项工作,我决定对每个权重的各种可能值进行优化,因此微软解决方案基金会.
要优化的内容:错误等级.我的测量标准化,从零到一.随着集成的进行以反映函数值,变化等的近期平均值,这些错误值会逐步修改.因此,我不是要创建一个产生实际错误值的函数,而是生成一个对其进行排序的数字.真正的错误,即如果所有采样点都按此估计误差值排序,则它们应该接收与按真实误差排序时所接收的等级相似的等级.
并非所有点都相同.如果#1真正错误的点区域排名#1000(反之亦然),我非常在意,但如果#500点排名#1000,则非常小心.我衡量成功的标准是在算法执行的中途将许多区域的以下总和最小化:
ABS(Log2(trueErrorRank) - Log2(estimatedErrorRank))
对于Log2,我使用的函数返回小于或等于数字的最大2的幂.从这个定义来看,得到有用的结果.交换#1和#2需要花费一分,但交换#2和#3不需要任何费用.这具有将点分为两个范围的幂的效果.在范围内交换的点不会添加到该函数中.
我如何评估.我已经构建了一个名为Rank的类来执行此操作:
按真实误差对所有地区排名一次.
对于每组独立的参数化权重,它计算该区域的试验(估计)误差.
按试验错误对区域进行排序.
计算每个地区的试验等级.
添加两个等级的日志的绝对差异,并将其称为参数化的值,因此要最小化的值.
C#代码.完成所有这些后,我只需要一种方法来设置Microsoft Solver Foundation以找到最佳参数.语法让我难过.这是我到目前为止的C#代码.在其中,您将看到我已经确定的三个问题的评论.也许你可以发现更多!任何想法如何使这项工作?
public void Optimize()
{
// Get the parameters from the GUI and figures out the low and high values for each weight.
ParseParameters();
// Computes the true rank for each region according to true error.
var myRanker = new Rank(ErrorData, false);
// Obtain Microsoft Solver Foundation's core solver object.
var solver = SolverContext.GetContext();
var model = solver.CreateModel();
// Create a delegate that can extract the current value of each solver parameter
// and stuff it in to a double array so we can later use it to call LinearTrial.
Func<Model, double[]> marshalWeights = (Model m) =>
{
var i = 0;
var weights = new double[myRanker.ParameterCount];
foreach (var d in m.Decisions)
{
weights[i] = d.ToDouble();
i++;
}
return weights;
};
// Make a solver decision for each GUI defined parameter.
// Parameters is a Dictionary whose Key is the parameter name, and whose
// value is a Tuple of two doubles, the low and high values for the range.
// All are Real numbers constrained to fall between a defined low and high value.
foreach (var pair in Parameters)
{
// PROBLEM 1! Should I be using Decisions or Parameters here?
var decision = new Decision(Domain.RealRange(ToRational(pair.Value.Item1), ToRational(pair.Value.Item2)), pair.Key);
model.AddDecision(decision);
}
// PROBLEM 2! This calls myRanker.LinearTrial immediately,
// before the Decisions have values. Also, it does not return a Term.
// I want to pass it in a lambda to be evaluated by the solver for each attempted set
// of decision values.
model.AddGoal("Goal", GoalKind.Minimize,
myRanker.LinearTrial(marshalWeights(model), false)
);
// PROBLEM 3! Should I use a directive, like SimplexDirective? What type of solver is best?
var solution = solver.Solve();
var report = solution.GetReport();
foreach (var d in model.Decisions)
{
Debug.WriteLine("Decision " + d.Name + ": " + d.ToDouble());
}
Debug.WriteLine(report);
// Enable/disable buttons.
UpdateButtons();
}
Run Code Online (Sandbox Code Playgroud)
更新:我决定寻找另一个库作为后备,并找到了DotNumerics(http://dotnumerics.com/).他们的Nelder-Mead Simplex解算器很容易调用:
Simplex simplex = new Simplex()
{
MaxFunEvaluations = 20000,
Tolerance = 0.001
};
int numVariables = Parameters.Count();
OptBoundVariable[] variables = new OptBoundVariable[numVariables];
//Constrained Minimization on the intervals specified by the user, initial Guess = 1;
foreach (var x in Parameters.Select((parameter, index) => new { parameter, index }))
{
variables[x.index] = new OptBoundVariable(x.parameter.Key, 1, x.parameter.Value.Item1, x.parameter.Value.Item2);
}
double[] minimum = simplex.ComputeMin(ObjectiveFunction, variables);
Debug.WriteLine("Simplex Method. Constrained Minimization.");
for (int i = 0; i < minimum.Length; i++)
Debug.WriteLine(Parameters[i].Key + " = " + minimum[i].ToString());
Run Code Online (Sandbox Code Playgroud)
我需要的是将ObjectiveFunction实现为一个采用双数组的方法:
private double ObjectiveFunction(double[] weights)
{
return Ranker.LinearTrial(weights, false);
}
Run Code Online (Sandbox Code Playgroud)
我没有尝试过它来对付真实数据,但我在Excel中创建了一个模拟来设置测试数据并对其进行评分.从他们的算法返回的结果并不完美,但给出了一个非常好的解决方案.
这是我的TL; DR摘要:他不知道如何最小化返回值LinearTrial,这需要一系列双打.这个数组中的每个值都有自己的最小/最大值,并且他正在使用它进行建模Decisions.
如果这是正确的,那么您似乎可以执行以下操作:
double[] minimums = Parameters.Select(p => p.Value.Item1).ToArray();
double[] maximums = Parameters.Select(p => p.Value.Item2).ToArray();
// Some initial values, here it's a quick and dirty average
double[] initials = Parameters.Select(p => (p.Item1 + p.Item2)/2.0).ToArray();
var solution = NelderMeadSolver.Solve(
x => myRanker.LinearTrial(x, false), initials, minimums, maximums);
// Make sure you check solution.Result to ensure that it found a solution.
// For this, I'll assume it did.
// Value 0 is the minimized value of LinearTrial
int i = 1;
foreach (var param in Parameters)
{
Console.WriteLine("{0}: {1}", param.Key, solution.GetValue(i));
i++;
}
Run Code Online (Sandbox Code Playgroud)
该NelderMeadSolver在MSF 3.0是新的.该Solve静态方法"查找指定的函数的最小值"根据在MSF组件的文件(尽管MSDN文档是空白和表示错误的函数签名).
免责声明: 我不是MSF专家,但上面的工作对我和我的测试目标函数(总和权重).