Partials w.r.t 一个数组,其中数组的两个不同部分用于计算?

Partials w.r.t a single array where two different parts of the array are used in compute?

我有一个包含 (x,y) 坐标对的数组 a。该数组是组件的唯一输入。

要生成每个坐标对的角度数组,我使用 np.arctan2(a[1,:], a[0,:])

我现在不确定如何声明偏音。如果我使用两个输入(即 x_coordinates、y_coordinates),那么按照 .

定义分音会很容易

但是,由于我只有一个数组,所以我不确定该怎么做。我的第一个想法是:

y = a[1, :]
x = a[0, :]
partials['angles', 'a'] = x/(x**2 + y**2) - y/(x**2 + y**2)

但我怀疑这是不正确的,因为我遗漏了 arctan2 的全导数的 dx 和 dy 部分。

无需拆分输入数组即可解决此问题的最佳方法是什么?

这里 atheta 都是向量值。当 OpenMDAO 计算部分时,它会按 C 顺序(行优先).

展平输出和输入

假设我们在 5 个点计算 atan2。

所以在这种情况下,我们在 5 个点(雅可比中的 5 行)有一个标量输出,每个点有两个输入(雅可比中的 10 列)。

另外,每个atan2的计算只依赖于a中对应的(y, x)。所以我们期望只有雅可比矩阵第一行的前两个元素是非零的。第二行就是第3个和第4个元素,以此类推,这是一个带状结构。

有时在这种情况下,查看通过近似导数获得的偏导数雅可比行列式的结构很有用:

    import openmdao.api as om
    import numpy as np

    class Atan2Comp(om.ExplicitComponent):

        def initialize(self):
            self.options.declare('num_points', default=5, desc='number of points at which atan2 is computed')

        def setup(self):
            n = self.options['num_points']
            self.add_input('a', shape=(n, 2))
            self.add_output('theta', shape=(n,))

            self.declare_partials(of='theta', wrt='a')

        def compute(self, inputs, outputs):
            a = inputs['a']
            y = a[:, 0]
            x = a[:, 1]
            outputs['theta'] = np.arctan2(y, x)

        def compute_partials(self, inputs, partials, discrete_inputs=None):
                partials['theta', 'a'] = 1.0

    n = 5
    p = om.Problem(model=om.Group())
    ivc = p.model.add_subsystem('ivc', om.IndepVarComp(), promotes_outputs=['a'])
    ivc.add_output('a', shape=(n, 2))
    p.model.add_subsystem('atan2', Atan2Comp(num_points=n), promotes_inputs=['a'])

    p.setup()

    p.set_val('a', np.random.rand(n, 2))

    p.setup()
    p.run_model()

    with np.printoptions(linewidth=1024):
        p.check_partials()

这表明雅可比行列式的结构为:

----------------------------
Component: Atan2Comp 'atan2'
----------------------------
  atan2: 'theta' wrt 'a'
    Forward Magnitude : 7.071068e+00
         Fd Magnitude : 3.177623e+00 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 7.503866e+00 *

    Relative Error (Jfor  - Jfd) : 2.361472e+00 *

    Raw Forward Derivative (Jfor)
[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
 [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]

    Raw FD Derivative (Jfd)
[[ 0.93986629 -0.35863362  0.          0.          0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.72103352 -0.80351616  0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          2.06027062 -0.73005685  0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.48842487 -0.71201471  0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.          0.          1.22969926 -0.9404304 ]]

因此FD结果具有预期值。我们只是将 1 填入矩阵,所以很明显解析形式的值是错误的。让我们解决这个问题。

首先,让我们声明这个部分是稀疏的,因为它可以在性能上产生显着差异。

在我们的示例中,每个非零元素的列只是 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],有 5 个点。 每个非零元素所在的行重复 [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]。 declare_partials 调用因此变为:

cs = np.arange(2 * n, dtype=int)
rs = np.repeat(np.arange(n, dtype=int), 2)
self.declare_partials(of='theta', wrt='a', rows=rs, cols=cs)

现在,在 compute_partials 中,我们需要将 y 的部分放在对角线上(基于我在 a 中对 y、x 的排序方式)。由于它被声明为稀疏导数,这相当于将 y 的部分放在部分 .

的偶数索引元素上
            def compute_partials(self, inputs, partials, discrete_inputs=None):
                a = inputs['a']
                y = a[:, 0]
                x = a[:, 1]

                # The partials wrt y go on the even-index elements of the sparse partial.
                partials['theta', 'a'][::2] = x / (x**2 + y**2)

                # The partials wrt x go on the odd-index elements of the sparse partial.
                partials['theta', 'a'][1::2] = -y / (x**2 + y**2)

实施这些更改后,check_partials 的结果显示

----------------------------
Component: Atan2Comp 'atan2'
----------------------------
  atan2: 'theta' wrt 'a'
    Forward Magnitude : 3.326988e+00
         Fd Magnitude : 3.326985e+00 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 3.489868e-06 *

    Relative Error (Jfor  - Jfd) : 1.048958e-06 *

    Raw Forward Derivative (Jfor)
[[ 1.55572771 -1.03761371  0.          0.          0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          1.05925649 -0.05293248  0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          1.71871308 -1.07781134  0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.99912434 -0.1373792   0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.          0.          1.13627733 -0.15229154]]

    Raw FD Derivative (Jfd)
[[ 1.5557261  -1.03761209  0.          0.          0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          1.05925643 -0.05293242  0.          0.          0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          1.71871122 -1.07780948  0.          0.          0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.9991242  -0.13737906  0.          0.        ]
 [ 0.          0.          0.          0.          0.          0.          0.          0.          1.13627715 -0.15229136]]

请注意,如果 'a' 的 "vector" 维度不是第一个轴,您将有不同的稀疏模式,但适用相同的过程。