def somatic_motion_update(D_up, D_cur, X, V):
    "\n    Compute updated positions by averaging pairwise difference vectors for\n    mutually visible pairs with equal bidirectional adjustments within each\n    pair. The updated distance matrix does not need to be symmetric; it\n    represents 'desired' updates based on recurrent learning.\n\n    :D_up: R(N,N)-matrix of updated distances\n    :D_cur: R(N,N)-matrix of current distances\n    :X: R(N,2)-matrix of current positions\n    :V: {0,1}(N,2)-matrix of current agent visibility\n    :returns: R(N,2)-matrix of updated positions\n    "
    N = len(X)
    D_shape = (N, N)
    if DEBUGGING:
        _check_ndim('X', X, 2)
        _check_shape('X', X, 2, axis=1)
        _check_shape('D_up', D_up, D_shape)
        _check_shape('D_cur', D_cur, D_shape)
        _check_shape('V', V, D_shape)
    XX = empty((N, N, 2))
    XX[:] = X[:, AX, :]
    XT = swapaxes(XX, 0, 1)
    D_inf = (D_up == inf)
    norm = (V * (~ D_inf))
    N = norm.sum(axis=1)
    valid = N.nonzero()[0]
    norm[valid] /= (2 * N[(valid, AX)])
    D_up[D_inf] = D_cur[D_inf] = 0.0
    DX = (XX - XT)
    DX_norm = hypot(DX[(..., 0)], DX[(..., 1)])
    valid = DX_norm.nonzero()
    DX[valid] /= DX_norm[valid][:, AX]
    return ((norm[(..., AX)] * (D_up - D_cur)[(..., AX)]) * DX).sum(axis=1)
Compute updated positions by averaging pairwise difference vectors for
mutually visible pairs with equal bidirectional adjustments within each
pair. The updated distance matrix does not need to be symmetric; it
represents 'desired' updates based on recurrent learning.

:D_up: R(N,N)-matrix of updated distances
:D_cur: R(N,N)-matrix of current distances
:X: R(N,2)-matrix of current positions
:V: {0,1}(N,2)-matrix of current agent visibility
:returns: R(N,2)-matrix of updated positions
def somatic_motion_update(D_up, D_cur, X, V):
    "\n    Compute updated positions by averaging pairwise difference vectors for\n    mutually visible pairs with equal bidirectional adjustments within each\n    pair. The updated distance matrix does not need to be symmetric; it\n    represents 'desired' updates based on recurrent learning.\n\n    :D_up: R(N,N)-matrix of updated distances\n    :D_cur: R(N,N)-matrix of current distances\n    :X: R(N,2)-matrix of current positions\n    :V: {0,1}(N,2)-matrix of current agent visibility\n    :returns: R(N,2)-matrix of updated positions\n    "
    N = len(X)
    D_shape = (N, N)
    if DEBUGGING:
        _check_ndim('X', X, 2)
        _check_shape('X', X, 2, axis=1)
        _check_shape('D_up', D_up, D_shape)
        _check_shape('D_cur', D_cur, D_shape)
        _check_shape('V', V, D_shape)
    XX = empty((N, N, 2))
    XX[:] = X[:, AX, :]
    XT = swapaxes(XX, 0, 1)
    D_inf = (D_up == inf)
    norm = (V * (~ D_inf))
    N = norm.sum(axis=1)
    valid = N.nonzero()[0]
    norm[valid] /= (2 * N[(valid, AX)])
    D_up[D_inf] = D_cur[D_inf] = 0.0
    DX = (XX - XT)
    DX_norm = hypot(DX[(..., 0)], DX[(..., 1)])
    valid = DX_norm.nonzero()
    DX[valid] /= DX_norm[valid][:, AX]
    return ((norm[(..., AX)] * (D_up - D_cur)[(..., AX)]) * DX).sum(axis=1)