Вопросы, касающиеся инициализации измерения нескольких массивов numpy в одном массиве numpy - PullRequest
0 голосов
/ 05 июля 2019

Учитывая, что у нас есть 3 матрицы Паули , каждая с размерностью (2x2). Как показано ниже:

X = np.array([[0, 1], [1, 0]], dtype=complex)
Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
Z = np.array([[1, 0], [0, -1]], dtype=complex)

Теперь, если я помещу эти отдельные (2x2) матрицы как записи в другие (2x2) матрицы. Скажи:

A = np.array([[X, 0], [0, Y]])
B = np.array([[X, X], [Y, Y]])

Странно, у A тусклость (2x2) - что в идеале то, что я хочу - и у B тусклость (2, 2, 2, 2), что бы это ни было, как показано ниже

A = np.array([[X, 0], [0, Y]])

A.shape
Out[131]: (2, 2)

B = np.array([[X, X], [Y, Y]])

B.shape
Out[133]: (2, 2, 2, 2)

С другой стороны, допустим, что C - матрица (1x3), а D - матрица (1x2), например,

C = np.array([[X, Y, 0]])
D = np.array([[X, Y]])

снова, если мы посмотрим на размеры инициализированных матриц

C = np.array([[X, Y, 0]])

C.shape
Out[136]: (1, 3)

D = np.array([[X, Y]])

D.shape
Out[138]: (1, 2, 2, 2)

Так что кажется, что всякий раз, когда я инициализирую массивы в массиве, подобном этому, если есть смешанный тип данных в виде записей, то есть матриц и целых чисел, как в A и C, это дает мне разумную форму, которую я хочу, т.е. измерение (2,2), с «скрытыми» размерами (2x2) для каждой записи. Но как только записи являются просто строго матрицами, как в B и D, это дает мне незаметное измерение, такое как (2, 2, 2, 2). Итак, мой вопрос:

Как я могу инициализировать массив (n, n) numpy (матрицу) со строго (2, 2) матрицами в качестве записей, и при этом сохранить его размерность (n, n), т.е. вместо того, чтобы давать мне какое-то странное измерение (w, x, y, z)? 1036 *

Причина, по которой я хочу этого, заключается в том, что я делаю вычисления с операторами в квантовой механике, с этими матрицами Паули, такими как X, Y и Z, в качестве квантовых элементов в квантовых вычислениях. Так что, если у меня есть состояние rho, которое также является (2x2) матрицей. Пусть

rho = np.array([[1, 0],
                [0, 0]])

И пусть RHO будет диагональная матрица (2x2), а записи - это матрицы (2x2) rho.

RHO = np.array([[rho, 0],
                [0, rho]])

Я хочу вычислить что-то вроде np.dot(D, RHO) так, чтобы оно давало

np.array([np.dot(X, rho), 0],
         [0, np.dot(Y, rho)])

И я проверил на python, что точечные произведения двух (2x2) матриц с (2x2) матрицами в качестве записей, умножения на их записи тоже будут точечными.

Моя мотивация для всего того, о чем я говорил выше, заключается в том, что я надеюсь использовать эти свойства в качестве средства для векторизации моего алгоритма. В настоящее время очень грубый пример моего алгоритма выглядит примерно так:

for i in (integer_1):
    for j in (integer_2):
        #do something that involves operations with sums of dot products of many matrices#

и векторизовать его так, чтобы он потенциально мог стать

for i in (integer_1):
        #do something that involves operations with multiples of matrices with dot product of matrices as its entries#

Что потенциально может работать или нет! Но мне любопытно посмотреть, даст ли мой метод ускорение. Надеюсь, я хорошо объяснил свои проблемы. Заранее спасибо.

Edit (1)

Я добавил математику в латексном формате, поэтому, надеюсь, вы поймете, что я пытаюсь сделать.

def compute_channel_operation(rho, operators):
    """
    Given a quantum state's density function rho, the effect of the
    channel on this state is:
    rho -> sum_{i=1}^n E_i * rho * E_i^dagger
    Args:
        rho (2x2 matrix): Density function
        operators (list): List of operators(matrices)
    Returns:
        number: The result of applying the list of operators
    """
    operation = [E@rho@E.conj().T for i, E in enumerate(operators)]
    return np.sum(operation, axis=0)

Итак, я надеялся, что вместо использования циклов это прямое умножение тензорного метода может быть быстрее, когда я масштабирую моё моделирование, скажем, нужно сделать это миллион раз Дело в том, что здесь K должен быть список любых (1xn) измерений, то есть [I] или [I, X] или [I, X, Y] или [I, X, Y, Z]. Я понимаю, что здесь X = X ^ {\ dagger}, как и Y и Z, но в моей симуляции будут ситуации, когда этого не произойдет.

Надеюсь, теперь я все объяснил ясно.

Ответы [ 2 ]

1 голос
/ 05 июля 2019

(2, 2, 2, 2) - это не странное измерение , это всего лишь 4-мерный тензор формы 2x2x2x2

Причина, по которой вы выбираете разные формы для A и B, заключается в том, что вы устанавливаете A со скалярным 0 вместо нулевой матрицы 2x2. Измените его на

A = np.array([[X, np.zeros((2, 2))], [np.zeros((2, 2)), Y]])
B = np.array([[X, X], [Y, Y]])

И вы получите 2x2x2x2 тензора для обоих.

Или измените его на

C = np.vstack([
    np.hstack([X, np.zeros((2, 2))]),
    np.hstack([np.zeros((2, 2)), Y])
])
D = np.vstack([
    np.hstack([X, X]),
    np.hstack([Y, Y])
])

И вы получите матрицы 4х4.

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

E = A.transpose(0, 2, 1, 3).reshape(4, 4)
F = B.transpose(0, 2, 1, 3).reshape(4, 4)

np.allclose(C, E)  # True
np.allclose(D, F)  # True

и обратно

G = E.reshape(2, 2, 2, 2).transpose(0, 2, 1, 3)
H = F.reshape(2, 2, 2, 2).transpose(0, 2, 1, 3)

np.allclose(A, G)  # True
np.allclose(B, H)  # True

РЕДАКТИРОВАТЬ: Относительно вашей функции compute_channel_operation(), вы можете значительно ускорить ее, если вы не выполняете понимание списка, но векторизуете операцию и передаете в 3D-тензоре все свои операции

rho = np.random.rand(2, 2)
operators = [np.random.rand(2, 2) for _ in range(1000)]
operators_tensor = np.asarray(operators)  # same as np.random.rand(1000, 2, 2)

def compute_channel_operation(rho, operators):
    operation = [E@rho@E.conj().T for i, E in enumerate(operators)]
    return np.sum(operation, axis=0)

def compute_channel_operation2(rho, operators):
    return np.sum(operators @ rho @ operators.transpose(0, 2, 1).conj(), axis=0)

A = compute_channel_operation(rho, operators)
B = compute_channel_operation(rho, operators_tensor)
C = compute_channel_operation2(rho, operators_tensor)

np.allclose(A, B) # True
np.allclose(A, C) # True

%timeit compute_channel_operation(rho, operators)
# 6.95 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit compute_channel_operation(rho, operators_tensor)
# 7.53 ms ± 141 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit compute_channel_operation2(rho, operators_tensor)
# 416 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
0 голосов
/ 05 июля 2019
In [343]: X = np.array([[0, 1], [1, 0]], dtype=complex) 
     ...: Y = np.array([[0, -1j], [1j, 0]], dtype=complex) 
     ...: Z = np.array([[1, 0], [0, -1]], dtype=complex)                                                        
In [344]: X                                                                                                     
Out[344]: 
array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]])
In [345]: Y                                                                                                     
Out[345]: 
array([[ 0.+0.j, -0.-1.j],
       [ 0.+1.j,  0.+0.j]])
In [346]: Z                                                                                                     
Out[346]: 
array([[ 1.+0.j,  0.+0.j],
       [ 0.+0.j, -1.+0.j]])

np.array пытается создать многомерный числовой массив, и в случае неудачи получается массив типа dtype (или возникает ошибка).

In [347]: np.array([X,0])                                                                                       
Out[347]: 
array([array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]]), 0], dtype=object)

Этот массив в основном совпадает со списком[X,0] - он содержит указатели или ссылки на объекты X и число 0.

Но, учитывая 2 (или более) массива, которые соответствуют по размеру, он создает числовой массив более высокого измерения.Например, массив (2,2,2) со сложным типом dtype.

In [348]: np.array([X,Y])                                                                                       
Out[348]: 
array([[[ 0.+0.j,  1.+0.j],
        [ 1.+0.j,  0.+0.j]],

       [[ 0.+0.j, -0.-1.j],
        [ 0.+1.j,  0.+0.j]]])

block или некоторая комбинация concatenate/stack может создать двумерный массив.Например, (2,4) сложный массив:

In [349]: np.block([X,Y])                                                                                       
Out[349]: 
array([[ 0.+0.j,  1.+0.j,  0.+0.j, -0.-1.j],
       [ 1.+0.j,  0.+0.j,  0.+1.j,  0.+0.j]])

Чтобы создать массив dtype объекта, содержащий X и Y`, я могу сделать:

In [356]: xy = np.empty((2,), object)                                                                           
In [357]: xy[0]= X                                                                                              
In [358]: xy[1]= Y                                                                                              
In [359]: xy                                                                                                    
Out[359]: 
array([array([[0.+0.j, 1.+0.j],
       [1.+0.j, 0.+0.j]]),
       array([[ 0.+0.j, -0.-1.j],
       [ 0.+1.j,  0.+0.j]])], dtype=object)

This empty с последующим индивидуальным присваиванием является наиболее надежным способом построения массива dtype объекта.Он обходит многомерный результат, показанный в Out[348].

Я не знаю, помогает ли какой-либо из этих подходов в ваших расчетных целях.Я недостаточно изучил ваше описание.Но имейте в виду, что быстрый код numpy работает с многомерными числовыми (включая complex) массивами (такими как Out[348]).Математика, использующая объектные массивы типа d, является хитом или промахом и почти всегда медленнее.

@ умножение матриц работает с X, Y, Out[348], rho и т. Д., Но не сOut[347] или RHO.+ работает с массивами объектов dtype при условии, что сами элементы поддерживают добавление.

...