Numpy einsum fonksiyonu

Einstein toplam gösterimi tekrar eden indislerin toplandığını varsayarak toplam işaretlerini yazma zorunluluğunu kaldırıyor.

Örneğin sismolojide moment tensör yoğunluğu elastik tensör (C), kayma vektörü (u) ve fay normali (v) cinsinden aşağıdaki gibi yazılabiliyor.

$$ M_{kl} = C_{ijkl} u_i v_j $$

Elastik tensör izotropik ortam için aşağıdaki gibi yazılabiliyor.

$$ C_{ijkl} = \lambda \delta_{ij} \delta_{kl}+ \mu(\delta_{ik}\delta_{jl} + \delta_{il}\delta_{jk}) $$

Bunları kodlamak isteğinizde ise yazılmayan toplam işaretleri size for döngüleri olarak geri dönüyor. İlk yazdığım versiyonda elastik tensörü fonksiyon olarak yazarak onun elemanlarını tek tek hesaplamaktan kurtulmuşum. Sıra moment tensöre geldiğinde ise 4 tane iç içe döngü yazmak zorunda kalmışım.

def generate_moment_tensor(u, v):
   """Generates and returns moment tensor from given u and v vectors
   for isotropic medium.

   Args:
       u (list): slip vector
       v (list): fault plane normal
   """
   mu = 1
   lmbda = 1

   M = np.zeros((3, 3))

   d = np.eye(3)  # kronecker delta

   C = lambda i, j, k, l: lmbda*d[i, j]*d[k, l]  \
       + mu*(d[i, k]*d[j, l] + d[i, l]*d[j, k])

   # M_kl = u_i v_j C_ijkl
   for k in range(3):
       for l in range(3):
           total = 0
           for i in range(3):
               for j in range(3):
                   total += u[i]*v[j]*C(i, j, k, l)
           M[k, l] = total

    return M

Sonradan numpy in einsum adlı bir fonksiyonu olduğunu öğrendim. Bu fonksiyonu kullanarak yazınca en azından görünüşte for döngülerini yazmak zorunda kalmıyorsunuz. Yalnız C için birkaç einsum kullanmak zorunda kaldım. Yazdığım şekilde çalışıyor ama bunu kronecker deltanın simetrisine borçluyum sanırım. Böyle birkaç toplam şeklinde yazılan değerler için belki fonksiyon yazmak daha doğru. C okunabilirliği de düştü diye düşünüyorum ama moment tensörünki fena durmuyor.

import numpy as np

def generate_moment_tensor_compact(u, v):
    """Generates and returns moment tensor from given u and v vectors
    for isotropic medium.

    Args:
        u (list): slip vector
        v (list): fault plane normal
    """
    mu = 1
    lmbda = 1
    d = eye(3) #kronecker delta
    C = lmbda*np.einsum('ij,kl', d, d) + mu*(np.einsum('ik,jl', d, d)+np.einsum('il,jk', d, d))

    return np.einsum('ijkl, i, j', C, u, v)