有哪些简单的方法可以自适应地对 2D 函数进行采样?

计算科学 自适应网格细化
2021-12-17 21:27:54

我有一个二维函数f(x,y)我想采样谁的值。该函数的计算成本非常高,并且它具有复杂的形状,因此我需要找到一种方法来使用最少数量的样本点获取有关其形状的最多信息。

有什么好的方法可以做到这一点?

到目前为止我所拥有的

  • 我从已经计算出函数值的现有点集开始(这可能是点的方格或其他点)。

  • 然后我计算这些点的 Delaunay 三角剖分。

  • 如果 Delaunay 三角剖分中的两个相邻点足够远 (>ΔX) 并且函数值在它们之间有很大的不同 (>Δf),然后我在它们中间插入一个新点。我对每个相邻的点对都这样做。

这种方法有什么问题?

好吧,它的效果相对较好,但在与此类似的功能上,它并不理想,因为样本点往往会“跳过”山脊,甚至不会注意到它的存在。

数学图形

它会产生这样的结果(如果初始点网格的分辨率足够粗糙):

数学图形

上图显示了计算函数值的点(实际上是它们周围的 Voronoi 细胞)。

数学图形

上图显示了从相同点生成的线性插值,并将其与 Mathematica 的内置采样方法(对于大约相同的起始分辨率)进行了比较。

如何改进它?

我认为这里的主要问题是我的方法根据梯度决定是否添加细化点。

添加细化点时最好考虑曲率或至少二阶导数。

问题

当我的点的位置根本不受限制时,考虑二阶导数或曲率的一种非常简单的实现方法是什么?(我不一定有一个方格的起点,理想情况下这应该是一般的。)

或者还有哪些其他简单的方法可以以最佳方式计算细化点的位置?

我打算在 Mathematica 中实现这个,但是这个问题主要是关于方法的。对于“易于实现”的部分,我确实在使用 Mathematica(也就是说,到目前为止这很容易做到,因为它有一个用于进行 Delaunay 三角测量的包)

我将此应用于什么实际问题

我正在计算相图。它具有复杂的形状。在一个区域中,它的值为 0,在另一个区域中,它介于 0 和 1 之间。这两个区域之间有一个急剧的跳跃(它是不连续的)。在函数大于零的区域中,存在一些平滑变化和一些不连续性。

函数值是基于 Monte Carlo 模拟计算的,因此有时会出现不正确的函数值或噪声(这种情况很少见,但对于大量点,它会发生,例如,由于由于一些随机因素)

已经在 Mathematica.SE 上问过这个问题,但我无法链接到它,因为它仍处于私人测试阶段。这里的问题是关于方法,而不是实现。


回复@suki

这是您建议的划分类型,即在三角形中间放置一个新点吗?

数学图形 数学图形 数学图形 数学图形

我在这里担心的是,它似乎需要在区域边缘进行特殊处理,否则会产生非常长且非常细的三角形,如上图所示。你纠正了吗?

更新

我描述的方法和@suki 建议基于三角形进行细分并将细分点放在三角形内时出现的一个问题是,当存在不连续性时(如我的问题),在一个步骤后重新计算 Delaunay 三角剖分可能导致三角形发生变化,并且可能出现一些在三个顶点具有不同函数值的大三角形。

这里有两个例子:

前一 ex2

第一个显示了围绕直线间断进行采样时的最终结果。第二个显示了类似情况的采样点分布。

有什么简单的方法可以避免这种情况?目前,我只是细分那些在重新三角剖分后消失的 egdes,但这感觉像是一个 hack,需要小心完成,因为在对称网格(如方形网格)的情况下,有几个有效的 Delaunay 三角剖分,因此边缘可能会改变重测后随机。

2个回答

不久前,我曾研究过与此类似的问题。

我认为我们的实现之间的主要区别在于我根据三角形而不是边缘来选择添加点的位置。我还在三角形内部而不是边缘上选择新点。

我觉得在三角形内添加点会通过稍微增加从旧点到新点的平均距离来提高效率。

无论如何,使用三角形而不是边缘的另一个好处是它给出了梯度向量的估计,而不是沿着这个特定边缘的斜率。

在我的 matlab 代码中,我使用了一个基类来处理大部分机器,并带有一些抽象方法:

  • weight(self)决定接下来要细分哪些三角形的优先级。
  • choosePoints(self,npoints = "auto")根据每个三角形的权重决定要评估的新点。

我发现这个设置非常灵活:

  • 将子类的weight()函数设置为三角形的面积会产生恒定的网格密度。
  • 设置weight()计算平均函数值乘以三角形面积给出了一种准随机概率采样。
  • 对于具有二进制输出的函数,使用var(triangle.zs)可以做,我觉得是将二等分搜索推广到超过一维。
  • usingarea + var(triangle.zs)非常有效地在任何地方放置一个恒定的密度,并沿着任何斜坡增加密度(几乎是你现在所拥有的)。

我使用 z 值的方差来近似一阶效应(斜率)的重要性,因为方差永远不会像斜率那样达到无穷大。

对于最后一个示例,背景密度很好,因为我在低价值空间中寻找高价值的不连续斑点。所以它会慢慢填充整个网格,当它找到一个斑点时,它会一直专注于跟随斑点的边缘,因为我在渐变上施加了很高的权重(而且它只填充了顶部的n三角形每次迭代)。最后,我可以知道没有(形状合理的)斑点(或我的斑点中的孔)的大小大于产生的背景网格密度。

像你一样,我的结果确实有一些不好的点,它们对我来说不是问题,因为错误是这样的,如果你重新运行附近的点,他们可能会给出正确的答案。我最终会在我的坏点周围增加网格密度。

无论你做什么,我总是建议制作与三角形大小相关的权重,以便在其他条件相同的情况下,首先分解大三角形。

也许您的解决方案是将我的方法更进一步,而不是根据该三角形单元格的内容评估三角形,而是根据该三角形和所有三个相邻三角形进行评估。

这将包含足够的信息来估计完整的 Hessian 矩阵。z = c1*x + C2*y c11*x^2+c12*x*y+c22*y^2您可以通过对感兴趣的三角形中的所有顶点进行最小二乘拟合来获得它(首先将坐标系置于三角形的中心)。

我不会直接使用梯度或 Hessian(那些常数),因为它们会在不连续处达到无穷大。

也许 z 值相对于这些点的平面近似的平方和误差将是衡量二阶效应有多有趣的有用度量。


更新:

这在我看来是合理的。

我从来没有真正解决过边缘的特殊情况。这让我有点困扰,但对于我正在做的事情,从边缘周围的很多点开始就足够了。

更优雅的是结合我们的两种方法,加权边和三角形。然后如果边太长,把它切成两半......我喜欢这个概念推广到更高维度的方式(但数字会很快变大)......

但是由于您不希望网格的主体具有高纵横比的三角形,因此您可以使用 Matlab 的freeboundary函数之类的函数来找到边界,然后在边界上以少一维运行相同的算法。如果操作正确,例如在立方体上,您可以在立方体的边缘、面和内部获得相同的网格密度。有趣的。

我从未找到一个好的解决方案的一件事是,我的版本永远不会在初始点集的凸包之外进行探索。

我认为你的启发式的主要问题是你只考虑一维的梯度,因此,在 dfdx 很小但 dfdy 很大的区域(就像你的例子中间发生的那样),你会在寻找时错过点在“错误”的维度。

一个快速的解决方法是考虑四个点的集合,取它们的重心,并逼近 |dfdx|+|dfdy| 使用这四点。另一种选择是取三个点(即一个三角形)并在它们上面取表面的最大梯度。