2016-12-01 14 views
7

я трансформировал следующую функциюNumpy: сделать версию пакетного умножения кватернионов

def quaternion_multiply(quaternion0, quaternion1): 
    """Return multiplication of two quaternions. 

    >>> q = quaternion_multiply([1, -2, 3, 4], [-5, 6, 7, 8]) 
    >>> numpy.allclose(q, [-44, -14, 48, 28]) 
    True 

    """ 
    x0, y0, z0, w0 = quaternion0 
    x1, y1, z1, w1 = quaternion1 
    return numpy.array((
     x1*w0 + y1*z0 - z1*y0 + w1*x0, 
     -x1*z0 + y1*w0 + z1*x0 + w1*y0, 
     x1*y0 - y1*x0 + z1*w0 + w1*z0, 
     -x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=numpy.float64) 

к дозируемой версии

def quat_multiply(self, quaternion0, quaternion1): 
    x0, y0, z0, w0 = np.split(quaternion0, 4, 1) 
    x1, y1, z1, w1 = np.split(quaternion1, 4, 1) 

    result = np.array((
     x1*w0 + y1*z0 - z1*y0 + w1*x0, 
     -x1*z0 + y1*w0 + z1*x0 + w1*y0, 
     x1*y0 - y1*x0 + z1*w0 + w1*z0, 
     -x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=np.float64) 
    return np.transpose(np.squeeze(result)) 

Эта функция обрабатывает quaternion1 и quaternion0 с формой (4?). Теперь я хочу, чтобы функция могла обрабатывать произвольное количество измерений, таких как (?,?, 4). Как это сделать?

ответ

2

Вы можете получить следующее поведение, просто пройдя axis-=-1 до np.split, чтобы разделить вдоль последней оси.

И поскольку ваши массивы имеют то, что раздражает размер 1 отставая размер, а не укладки вдоль нового измерения, то сжимая, что один прочь, вы можете просто объединить их, снова вдоль (последний) axis=-1:

def quat_multiply(self, quaternion0, quaternion1): 
    x0, y0, z0, w0 = np.split(quaternion0, 4, axis=-1) 
    x1, y1, z1, w1 = np.split(quaternion1, 4, axis=-1) 
    return np.concatenate(
     (x1*w0 + y1*z0 - z1*y0 + w1*x0, 
     -x1*z0 + y1*w0 + z1*x0 + w1*y0, 
     x1*y0 - y1*x0 + z1*w0 + w1*z0, 
     -x1*x0 - y1*y0 - z1*z0 + w1*w0), 
     axis=-1) 

Обратите внимание, что при таком подходе, вы можете не только умножать идентичную форму кватернионных стек любого числа измерений:

>>> a = np.random.rand(6, 5, 4) 
>>> b = np.random.rand(6, 5, 4) 
>>> quat_multiply(None, a, b).shape 
(6, 5, 4) 

Но вы также можете получить хорошее вещание, что позволяет, т.е.умножьте стек кватернионов ни с одной без необходимости возиться с размерами:

>>> a = np.random.rand(6, 5, 4) 
>>> b = np.random.rand(4) 
>>> quat_multiply(None, a, b).shape 
(6, 5, 4) 

или с минимальным пустячный делать все перекрестные продукты между двумя стеками в одной строке:

>>> a = np.random.rand(6, 4) 
>>> b = np.random.rand(5, 4) 
>>> quat_multiply(None, a[:, None], b).shape 
(6, 5, 4) 
3

Вы можете использовать np.rollaxis, чтобы перенести последнюю ось на передний план, помогая нам нарезать 4 массива, фактически не разбивая их. Мы выполняем требуемые операции и, наконец, посылаем назад первую ось в конец, чтобы форма выходного массива была такой же, как и входы. Таким образом, мы получили бы решение для общих п-мерных ndarrays, как так -

def quat_multiply_ndim(quaternion0, quaternion1): 
    x0, y0, z0, w0 = np.rollaxis(quaternion0, -1, 0) 
    x1, y1, z1, w1 = np.rollaxis(quaternion1, -1, 0) 
    result = np.array((
     x1*w0 + y1*z0 - z1*y0 + w1*x0, 
     -x1*z0 + y1*w0 + z1*x0 + w1*y0, 
     x1*y0 - y1*x0 + z1*w0 + w1*z0, 
     -x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=np.float64) 
    return np.rollaxis(result,0, result.ndim) 

Пример запуска -

In [107]: # N-dim arrays 
    ...: a1 = np.random.randint(0,9,(2,3,2,4)) 
    ...: b1 = np.random.randint(0,9,(2,3,2,4)) 
    ...: 

In [108]: quat_multiply_ndim(a1,b1) # New ndim approach 
Out[108]: 
array([[[[ 154., 48., 55., -57.], 
     [ 31., 81., 29., -95.]], 

     [[ 31., 14., 88., 12.], 
     [ 3., 30., 20., -51.]], 

     [[ 104., 61., 102., -39.], 
     [ 0., 14., 14., -56.]]], 


     [[[ -28., 36., 24., -8.], 
     [ 11., 76., -7., -36.]], 

     [[ 54., 3., -2., -19.], 
     [ 52., 62., 15., -55.]], 

     [[ 76., 28., 28., -60.], <--------| 
     [ 14., 54., 13., 5.]]]])   | 
                | 
In [109]: quat_multiply(a1[1,2],b1[1,2]) # Old 2D approach 
Out[109]:           | 
array([[ 76., 28., 28., -60.], ------------------| 
     [ 14., 54., 13., 5.]]) 
+0

Вы могли бы спасти себя копию, просто переносив массив, а не используя 'np.rollaxis', чтобы перенести последнее измерение на передний план. –

+0

@ali_m Не создает ли 'np.rollaxis' представление? – Divakar

+0

Извините, вы правы (я, вероятно, сбивал с толку 'np.roll'). Однако Transpose все еще немного быстрее. –

1

Вы почти там! Вам просто нужно быть немного осторожным о том, как вы расщеплении и конкатенации ваш массив:

def quat_multiply(quaternion0, quaternion1): 
    x0, y0, z0, w0 = np.split(quaternion0, 4, axis=-1) 
    x1, y1, z1, w1 = np.split(quaternion1, 4, axis=-1) 

    return np.squeeze(np.stack((
     x1*w0 + y1*z0 - z1*y0 + w1*x0, 
     -x1*z0 + y1*w0 + z1*x0 + w1*y0, 
     x1*y0 - y1*x0 + z1*w0 + w1*z0, 
     -x1*x0 - y1*y0 - z1*z0 + w1*w0), axis=-1), axis=-2) 

Здесь мы используем axis=-1 оба раза расколоть вдоль последней оси, а затем сцепить назад вдоль последней оси , Наконец, мы выжимаем вторую-последнюю ось, как вы правильно заметили. И чтобы показать вам, что он работает:

>>> q0 = np.array([-5, 6, 7, 8]) 
>>> q1 = np.array([1, -2, 3, 4]) 
>>> q0 = np.tile(q1, (2, 2, 1)) 
>>> q0 
array([[[-5, 6, 7, 8], 
     [-5, 6, 7, 8]], 
     [[-5, 6, 7, 8], 
     [-5, 6, 7, 8]]]) 
>>> q1 = np.tile(q2, (2, 2, 1)) 
>>> q = quat_multiply(q0, q1) 
array([[[-44, -14, 48, 28], 
     [-44, -14, 48, 28]], 
     [[-44, -14, 48, 28], 
     [-44, -14, 48, 28]]]) 
>>> q.shape 
(2, 2, 4) 

Надеюсь, это то, что вам нужно! Это должно работать на произвольные размеры и произвольное число измерений.

Примечание:np.split, похоже, не работает над списками. Таким образом, вы можете передавать массивы только своей новой функции, как я уже говорил выше. Если вы хотите получать списки, вы можете позвонить

np.split(np.asarray(quaternion0), 4, -1) 

внутри вашей функции.

Кроме того, ваш тестовый пример кажется неправильным. Я думаю, что вы поменяли позиции quaternion0 и quaternion1: я поменял их назад, тестируя q0 и q1.