Làm cách nào để triển khai chức năng lập chỉ mục hiệu quả cho hai tích phân hạt <ij | kl>?


11

Đây là một vấn đề liệt kê đối xứng đơn giản. Tôi cung cấp nền tảng đầy đủ ở đây, nhưng không có kiến ​​thức về hóa học lượng tử là cần thiết.

Tích phân hai hạt là: i j | k l = ψ * i ( x ) ψ * j ( x ' ) ψ k ( x ) ψ l ( x ' )ij|kl Và nó có 4 đối xứng sau: i j | k l = j i | l k = k l | i j = l k | j i Tôi đã một hàm tính tích phân và lưu trữ chúng trong một mảng 1D, lập chỉ mục như sau:

ij|kl=ψi(x)ψj(x)ψk(x)ψl(x)|xx|d3xd3x
ij|kl=ji|lk=kl|ij=lk|ji
int2
int2(ijkl2intindex2(i, j, k, l))

trong đó hàm ijkl2intindex2trả về một chỉ mục duy nhất, tính đến các đối xứng ở trên. Yêu cầu duy nhất là nếu bạn lặp qua tất cả các kết hợp của i, j, k, l (từ 1 đến n mỗi), nó sẽ điền vào int2mảng liên tiếp và nó sẽ gán cùng một chỉ mục cho tất cả các kết hợp ijkl có liên quan ở trên 4 đối xứng.

Triển khai hiện tại của tôi ở Fortran là đây . Nó rất chậm. Có ai biết làm thế nào để làm điều này một cách hiệu quả? (Bằng bất kỳ ngôn ngữ nào.)

ψi(x)ikjl

ij|kl=ji|lk=kj|il=il|kj=
=kl|ij=lk|ji=il|kj=kj|il

ijkl(ij|kl)=ik|jljk


d3x

1
d3xdx1dx2dx3x=(x1,x2,x3)d3x

xx=(x1,x2,x3)dx

d3x

Câu trả lời:


5

[Chỉnh sửa: Lần thứ 4 là sự quyến rũ, cuối cùng là một điều hợp lý]

nn2(n2+3)t(t(n))+t(t(n1))t(a)at(a)=a(a+1)/2

ijtid(i,j)tid(k,l)tid(a,b)a,b

def ascendings(n):
    idx = 0
    for i in range(1,n+1):
        for j in range(1,i+1):
            for k in range(1,i):
                for l in range(1,k+1):
                    idx = idx + 1
                    print(i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                print(i,j,k,l)
    return idx

llk

t(t(n1))

def mixcendings(n):
    idx = 0
    for j in range(2,n+1):
        for i in range(1,j):
            for k in range(1,j):
                for l in range(1,k):
                    print(i,j,k,l)
                    idx = idx + 1
            k=j
            for l in range(1,i+1):
                print(i,j,k,l)
                idx = idx + 1
    return idx

Sự kết hợp của cả hai điều này mang lại cho bộ hoàn chỉnh, vì vậy việc đặt cả hai vòng lặp lại với nhau sẽ cho chúng ta bộ chỉ số hoàn chỉnh.

n

Trong python, chúng ta có thể viết iterator sau để cung cấp cho chúng ta các giá trị idx và i, j, k, l cho mỗi kịch bản khác nhau:

def iterate_quad(n):
    idx = 0
    for i in range(1,n+1):
        for j in range(1,i+1):
            for k in range(1,i):
                for l in range(1,k+1):
                    idx = idx + 1
                    yield (idx,i,j,k,l)
                    #print(i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                yield (idx,i,j,k,l)

    for i in range(2,n+1):
        for j in range(1,i):
            for k in range(1,i):
                for l in range(1,k):
                    idx = idx + 1
                    yield (idx,i,j,k,l)
            k=i
            for l in range(1,j+1):
                idx = idx + 1
                yield (idx,i,j,k,l)

in3+jn2+kn+l

integer function squareindex(i,j,k,l,n)
    integer,intent(in)::i,j,k,l,n
    squareindex = (((i-1)*n + (j-1))*n + (k-1))*n + l
end function

integer function generate_order_array(n,arr)
    integer,intent(in)::n,arr(*)
    integer::total,idx,i,j,k,l
    total = n**2 * (n**2 + 3)
    reshape(arr,total)
    idx = 0
    do i=1,n
      do j=1,i
        do k=1,i-1
          do l=1,k
            idx = idx+1
            arr(idx) = squareindex(i,j,k,l,n)
          end do
        end do
        k=i
        do l=1,j
          idx = idx+1
          arr(idx) = squareindex(i,j,k,l,n)
        end do
      end do
    end do

    do i=2,n
      do j=1,i-1
        do k=1,i-1
          do l=1,j
            idx = idx+1
            arr(idx) = squareindex(i,j,k,l,n)
          end do
        end do
        k=i
        do l=1,j
          idx = idx+1
          arr(idx) = squareindex(i,j,k,l,n)
        end do
      end do
    end do

    generate_order_array = idx
  end function

Và sau đó lặp lại nó:

maxidx = generate_order_array(n,arr)
do idx=1,maxidx
  i = idx/(n**3) + 1
  t_idx = idx - (i-1)*n**3
  j = t_idx/(n**2) + 1
  t_idx = t_idx - (j-1)*n**2
  k = t_idx/n + 1
  t_idx = t_idx - (k-1)*n
  l = t_idx

  ! now have i,j,k,l, so do stuff
  ! ...
end do

Xin chào Phil, cảm ơn rất nhiều vì câu trả lời! Tôi đã thử nó, và có hai vấn đề. Ví dụ: idx_all (1, 2, 3, 4, 4) == idx_all (1, 2, 4, 3, 4) = 76. Nhưng <12 | 34> / = <12 | 43>. Nó chỉ bằng nhau nếu các quỹ đạo là có thật. Vì vậy, giải pháp của bạn dường như dành cho trường hợp 8 đối xứng (xem ví dụ về Fortran của tôi ở trên để biết phiên bản đơn giản hơn, ijkl2intindex ()). Vấn đề thứ hai là các chỉ số không liên tiếp, tôi đã dán kết quả ở đây: gist.github.com/2703756 . Dưới đây là kết quả chính xác từ thói quen ijkl2intindex2 () của tôi ở trên: gist.github.com/2703767 .
Ondřej Čertík

1
@ OndřejČertík: Bạn muốn có một dấu hiệu liên quan? có sự bình tĩnh trở lại một dấu hiệu nếu bạn chuyển đổi thứ tự.
Deathbreath

OndřejČertík: Bây giờ tôi thấy sự khác biệt. Như @Deathbreath chỉ ra, bạn có thể phủ định chỉ mục, nhưng điều đó sẽ không rõ ràng cho vòng lặp tổng thể. Tôi sẽ suy nghĩ và cập nhật nó.
Phil H

Trên thực tế, việc phủ định chỉ mục sẽ không hoạt động hoàn toàn vì sự vô nghĩa sẽ khiến giá trị sai.
Phil H

<ij|kl>=<ji|kl>=<ij|lk>=<ji|lk>
ijkl[idxpair(indexij,indexkl,,)]signijsignkl

3

Dưới đây là ý tưởng sử dụng đường cong điền vào không gian đơn giản được sửa đổi để trả về cùng khóa cho các trường hợp đối xứng (tất cả các đoạn mã đều bằng python).

# Simple space-filling curve
def forge_key(i, j, k, l, n): 
  return i + j*n + k*n**2 + l*n**3

# Considers the possible symmetries of a key
def forge_key_symmetry(i, j, k, l, n): 
  return min(forge_key(i, j, k, l, n), 
             forge_key(j, i, l, k, n), 
             forge_key(k, l, i, j, n), 
             forge_key(l, k, j, i, n)) 

Ghi chú:

  • Ví dụ là python nhưng nếu bạn sắp xếp các hàm vào mã fortran của bạn và bỏ kiểm soát vòng lặp bên trong của bạn cho (i, j, k, l), bạn sẽ có được hiệu suất tốt.
  • Bạn có thể tính toán khóa bằng cách sử dụng float và sau đó chuyển đổi khóa thành số nguyên để sử dụng làm chỉ mục, điều này sẽ cho phép trình biên dịch sử dụng các đơn vị dấu phẩy động (ví dụ AVX khả dụng).
  • Nếu N là lũy thừa của 2 thì phép nhân sẽ chỉ là một chút dịch chuyển.
  • Việc xử lý các đối xứng không hiệu quả trong bộ nhớ (nghĩa là nó không tạo ra một chỉ mục liên tục) và sử dụng khoảng 1/4 tổng số mục nhập mảng chỉ mục.

Dưới đây là một ví dụ thử nghiệm cho n = 2:

for i in range(n):
  for j in range(n):
    for k in range(n):
      for l in range(n):
        key = forge_key_symmetry(i, j, k, l, n)
        print i, j, k , l, key

Đầu ra cho n = 2:

i j k l key
0 0 0 0 0
0 0 0 1 1
0 0 1 0 1
0 0 1 1 3
0 1 0 0 1
0 1 0 1 5
0 1 1 0 6
0 1 1 1 7
1 0 0 0 1
1 0 0 1 6
1 0 1 0 5
1 0 1 1 7
1 1 0 0 3
1 1 0 1 7
1 1 1 0 7
1 1 1 1 15

Nếu quan tâm, hàm nghịch đảo của forge_key là:

# Inverse of forge_key
def split_key(key, n): 
  d = key / n**3
  c = (key - d*n**3) / n**2
  b = (key - c*n**2 - d*n**3) / n 
  a = (key - b*n - c*n**2 - d*n**3)
  return (a, b, c, d)

Ý của bạn là "nếu n là lũy thừa của 2" thay vì bội của 2?
Aron Ahmadia

vâng, cảm ơn Aron. Tôi đã viết câu trả lời này ngay trước khi đi ăn tối và Hulk đang viết.
fcruz

Tài giỏi! Tuy nhiên, không phải chỉ số tối đa n ^ 4 (hay n ^ 4-1 nếu bạn bắt đầu từ 0)? Vấn đề là ở kích thước cơ sở mà tôi muốn có thể thực hiện, nó sẽ không vừa với bộ nhớ. Với chỉ số liên tiếp, kích thước của mảng là n ^ 2 * (n ^ 2 + 3) / 4. Hừm, dù sao đó cũng chỉ bằng khoảng 1/4 kích thước đầy đủ. Vì vậy, có lẽ tôi không nên lo lắng về yếu tố 4 trong mức tiêu thụ bộ nhớ. Tuy nhiên, vẫn phải có một số cách để mã hóa chỉ mục liên tiếp chính xác bằng cách chỉ sử dụng 4 đối xứng này (tốt hơn so với giải pháp xấu xí của tôi trong bài đăng của tôi, nơi tôi cần thực hiện các vòng lặp kép).
Ondřej Čertík

Vâng, đúng vậy! Tôi không biết cách giải quyết một cách tao nhã (không sắp xếp và đánh số lại) chỉ mục nhưng thuật ngữ hàng đầu trong việc sử dụng bộ nhớ là O (N ^ 4). Hệ số 4 sẽ tạo ra sự khác biệt nhỏ trong bộ nhớ cho N. lớn
fcruz

0

Đây có phải không chỉ là khái quát của vấn đề lập chỉ mục ma trận đối xứng đóng gói? Giải pháp có offset (i, j) = i * (i + 1) / 2 + j, phải không? Bạn có thể nhân đôi điều này và lập chỉ mục một mảng 4D đối xứng không? Việc thực hiện đòi hỏi phân nhánh dường như không cần thiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.