두 입자 적분 <ij | kl>에 대해 효율적인 색인 기능을 구현하는 방법은 무엇입니까?


11

이것은 단순한 대칭 열거 문제입니다. 나는 여기에 완전한 배경을 제공하지만 양자 화학에 대한 지식은 필요하지 않습니다.

두 개의 입자 통합 이다 I J | 케이 L = ψ * I ( X ) ψ * J ( X ' ) ψ K ( X ) ψ (L) ( X ' )ij|kl 그리고 다음 4 대칭성을 가지고 I J를 | 케이 L = J I | 케이 = K L | I J = K | J I는 1 차원 어레이의 적분을 계산하여 저장하는 기능이있는다음과 같은 인덱스 :

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))

여기서 함수 ijkl2intindex2는 위의 대칭을 고려하여 고유 인덱스를 반환합니다. 유일한 요구 사항은 i, j, k, l (각각 1에서 n까지)의 모든 조합을 반복하면 int2배열을 연속적으로 채우고 위와 관련된 모든 ijkl 조합에 동일한 인덱스를 할당해야한다는 것입니다 4 개의 대칭.

Fortran에서 현재 구현 한 것이 여기 있습니다 . 매우 느립니다. 아무도 이것을 효과적으로하는 방법을 알고 있습니까? (모든 언어로)

ψ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

답변:


5

[편집 : 네 번째는 매력적이며, 마침내 뭔가 합리적입니다.]

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

이 두 가지의 조합은 완전한 세트를 제공하므로 두 루프를 함께 사용하면 완전한 인덱스 세트를 얻을 수 있습니다.

n

파이썬에서는 다음과 같은 반복자를 작성하여 각 시나리오마다 idx 및 i, j, k, l 값을 제공 할 수 있습니다.

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

그런 다음 루프를 반복하십시오.

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

안녕 Phil, 답변 주셔서 감사합니다! 테스트했는데 두 가지 문제가 있습니다. 예를 들어 idx_all (1, 2, 3, 4, 4) == idx_all (1, 2, 4, 3, 4) = 76. 그러나 <12 | 34> / = <12 | 43>. 궤도가 실제 인 경우에만 동일합니다. 따라서 귀하의 솔루션은 8 가지 대칭의 경우 인 것 같습니다 (더 간단한 버전 인 ijkl2intindex ()에 대해서는 위의 Fortran 예제를 참조하십시오). 두 번째 문제는 인덱스가 연속적 이지 않다는 것 입니다 .gist.github.com / 2703756에 결과를 붙여 넣었 습니다 . 위의 ijkl2intindex2 () 루틴의 올바른 결과는 다음과 같습니다. gist.github.com/2703767 .
Ondřej Čertík

1
@ OndřejČertík : 관련 사인을 원하십니까? 주문을 전환 한 경우 idxpair가 부호를 반환하도록하십시오.
Deathbreath

OndřejČertík : 지금 차이가 보입니다. @Deathbreath가 지적했듯이 인덱스를 부정 할 수는 있지만 전체 루프만큼 깨끗하지는 않습니다. 생각하고 업데이트하겠습니다.
Phil H

실제로, idxpair가 값을 잘못 얻을 수 있기 때문에 인덱스를 부정하면 완전히 작동하지 않습니다.
Phil H

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

3

다음은 대칭 사례에 대해 동일한 키를 반환하도록 수정 된 간단한 공간 채우기 곡선을 사용하는 아이디어입니다 (모든 코드 스 니펫은 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)) 

메모:

  • 예제는 파이썬이지만 함수를 포트란 코드에 인라인하고 내부 루프 (i, j, k, l)를 풀면 적절한 성능을 얻을 수 있습니다.
  • 부동 소수점을 사용하여 키를 계산 한 다음 인덱스로 사용하기 위해 키를 정수로 변환하면 컴파일러가 부동 소수점 단위를 사용할 수 있습니다 (예 : AVX 사용 가능).
  • N이 2의 거듭 제곱이면 곱셈은 비트 시프트입니다.
  • 대칭에 대한 처리는 메모리에서 비효율적이며 (즉, 연속 인덱싱을 생성하지 않음) 총 인덱스 배열 항목의 약 1/4을 사용합니다.

다음은 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

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

관심이 있다면 forge_key의 역함수는 다음과 같습니다.

# 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)

2의 배수 대신 "n이 2의 거듭 제곱 인 경우"를 의미 했습니까?
Aron Ahmadia

예, Aron에게 감사합니다. 나는 저녁 식사를 가기 직전에이 답변을 썼고 헐크는 글을 쓰고 있었다.
fcruz

영리한! 그러나 최대 인덱스 n ^ 4 (또는 0에서 시작하는 경우 n ^ 4-1)가 아닙니까? 문제는 내가 할 수있는 기본 크기의 경우 메모리에 맞지 않는다는 것입니다. 연속 인덱스를 사용하면 배열의 크기는 n ^ 2 * (n ^ 2 + 3) / 4입니다. Hm, 어쨌든 전체 크기의 약 1/4입니다. 따라서 메모리 소비에서 4의 요소에 대해 걱정하지 않아도됩니다. 그럼에도 불구 하고이 4 가지 대칭만을 사용하여 올바른 연속 인덱스를 인코딩 할 수있는 방법이 있어야합니다 (더블 루프를 수행 해야하는 게시물의 추악한 솔루션보다 낫습니다).
Ondřej Čertík

그래 맞아! 색인을 우아하게 해결하는 방법을 모르지만 (정렬 및 번호 매기기 없음) 메모리 사용의 주요 용어는 O (N ^ 4)입니다. 4의 인자는 큰 N에 대한 메모리의 작은 차이를 만들어야합니다.
fcruz

0

이것은 팩형 대칭 매트릭스 인덱싱 문제의 일반화가 아닙니까? 해결책은 offset (i, j) = i * (i + 1) / 2 + j입니다. 그렇지 않습니까? 이것을 두 배로 줄이고 이중 대칭 4D 배열을 색인 할 수 없습니까? 분기가 필요한 구현은 불필요 해 보입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.