Изменение формы тензоров в трехмерной матрице - PullRequest
0 голосов
/ 02 декабря 2018

Я, по сути, пытаюсь выполнить это , а затем это , но, скажем, с помощью трехмерной матрицы (128,128,60,6).Четвертое измерение - это вектор массива, который представляет диффузионный массив в этом вокселе, например:

d[30,30,30,:] = [dxx, dxy, dxz, dyy, dyz, dzz] = D_array

Где dxx и т. Д. - диффузия для определенного направления.D_array также можно рассматривать как треугольную матрицу (поскольку dxy == dyx и т. Д.).Таким образом, я могу использовать эти 2 других ответа, чтобы перейти от D_array к D_square, например:

D_square = [[dxx, dxy, dxz], [dyx, dyy, dyz],[dzx, dzy, dzz]]

Однако я не могу понять следующий шаг - как применить это преобразование единицы D_array в D_square длявесь трехмерный том.

Вот фрагмент кода, который работает с одним тензором:

#this solves an linear eq. that provides us with diffusion arrays at each voxel in a 3D space
D = np.einsum('ijkt,tl->ijkl',X,bi_plus)

#our issue at this point is we have a vector that represents a triangular matrix.
# first make a tri matx from the vector, testing on unit tensor first
D_tri = np.zeros((3,3))
D_array = D[30][30][30]
D_tri[np.triu_indices(3)] = D_array
# then getting the full sqr matrix
D_square = D_tri.T + D_tri
np.fill_diagonal(D_square, np.diag(D_tri))

Так что же было бы в общих чертах для формулировки этого единичного преобразования тензора диффузии ввесь объем 3D одновременно?

Ответы [ 2 ]

0 голосов
/ 02 декабря 2018

Векторизованный способ сделать это:

# Gets the triangle matrix
d_tensor = np.zeros(128, 128, 60, 3, 3)
triu_idx = np.triu_indices(3)
d_tensor[:, :, :, triu_idx[0], triu_idx[1]] = d
# Make it symmetric
diagonal = np.zeros(128, 128, 60, 3, 3)
idx = np.arange(3)
diagonal[:, :, :, idx, idx] = d_tensor[:, :, :, idx, idx]
d_tensor = np.transpose(d_tensor, (0, 1, 2, 4, 3)) + d_tensor - diagonal
0 голосов
/ 02 декабря 2018

Подход № 1

Вот пример использования row, col индексов от triu_indices для индексации по двум последним осям в инициализированный выходной массив -

def squareformnd_rowcol_integer(ar, n=3):
    out_shp = ar.shape[:-1] + (n,n)
    out = np.empty(out_shp, dtype=ar.dtype)

    row,col = np.triu_indices(n)

    # Get a "rolled-axis" view with which the last two axes come to the front
    # so that we could index into them just like for a 2D case
    out_rolledaxes_view = out.transpose(np.roll(range(out.ndim),2,0))    

    # Assign permuted version of input array into rolled output version
    arT = np.moveaxis(ar,-1,0)
    out_rolledaxes_view[row,col] = arT
    out_rolledaxes_view[col,row] = arT
    return out

Подход № 2

Еще один, последние две оси которого объединены в одну, а затем индексируются с помощью линейных индексов -

def squareformnd_linear_integer(ar, n=3):
    out_shp = ar.shape[:-1] + (n,n)
    out = np.empty(out_shp, dtype=ar.dtype)

    row,col = np.triu_indices(n)
    idx0 = row*n+col
    idx1 = col*n+row

    ar2D = ar.reshape(-1,ar.shape[-1])
    out.reshape(-1,n**2)[:,idx0] = ar2D
    out.reshape(-1,n**2)[:,idx1] = ar2D
    return out

Подход № 3

Наконец, в целом новый метод, использующий masking и должен быть лучше с производительностью, как большинство основанных на masking, когда дело доходит до индексации -

def squareformnd_masking(ar, n=3):
    out = np.empty((n,n)+ar.shape[:-1] , dtype=ar.dtype)

    r = np.arange(n)
    m = r[:,None]<=r

    arT = np.moveaxis(ar,-1,0)
    out[m] = arT
    out.swapaxes(0,1)[m] = arT
    new_axes = range(out.ndim)[2:] + [0,1]
    return out.transpose(new_axes)

Времена на (128,128,60,6) формеслучайный массив -

In [635]: ar = np.random.rand(128,128,60,6)

In [636]: %timeit squareformnd_linear_integer(ar, n=3)
     ...: %timeit squareformnd_rowcol_integer(ar, n=3)
     ...: %timeit squareformnd_masking(ar, n=3)
10 loops, best of 3: 103 ms per loop
10 loops, best of 3: 103 ms per loop
10 loops, best of 3: 53.6 ms per loop
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...