วิธีการใช้ฟังก์ชั่นการจัดทำดัชนีที่มีประสิทธิภาพสำหรับการรวมสองอนุภาค <ij | kl>


11

นี่เป็นปัญหาการแจงนับสมมาตรอย่างง่าย ฉันให้พื้นฐานเต็มรูปแบบที่นี่ แต่ไม่จำเป็นต้องมีความรู้เกี่ยวกับเคมีควอนตัม

อินทิกรัลสองอนุภาคผมJ|kล.คือ:

ผมJ|kล.=ψผม* * * *(x)ψJ* * * *(x')ψk(x)ψล.(x')|x-x'|d3xd3x'
และมันมี 4 สมมาตรดังต่อไปนี้:
ผมJ|kล.=Jผม|ล.k=kล.|ผมJ=ล.k|Jผม
ฉันมีฟังก์ชั่นที่คำนวณอินทิกรัลและเก็บไว้ในอาร์เรย์ 1Dint2โดยมีดัชนีดังนี้:
int2(ijkl2intindex2(i, j, k, l))

โดยที่ฟังก์ชันijkl2intindex2ส่งคืนดัชนีที่ไม่ซ้ำกันโดยคำนึงถึงความสมมาตรข้างต้น ข้อกำหนดเพียงอย่างเดียวคือถ้าคุณวนซ้ำการรวมกันทั้งหมดของ i, j, k, l (จาก 1 ถึง n แต่ละอัน) มันจะเติมint2อาเรย์อย่างต่อเนื่องและจะกำหนดดัชนีเดียวกันให้กับชุดค่าผสม ijkl ทั้งหมดที่เกี่ยวข้องโดยข้างต้น 4 สมมาตร

การดำเนินงานปัจจุบันของฉันใน Fortran เป็นที่นี่ มันช้ามาก ใครรู้วิธีการทำอย่างมีประสิทธิภาพ? (ในภาษาใด ๆ )

คำแนะนำ: ถ้าวงโคจรเป็นจริงจากนั้นนอกจากสมมาตรข้างต้นเราสามารถแลกเปลี่ยนi kและj lดังนั้นเราจึงได้ 8 สมมาตรทั้งหมด: i j | k l = j i | l k = k j | ฉันl = ฉันl | k j = = k l |ψผม(x)ผมkJล.

ij|kl=ji|lk=kj|il=il|kj=
แล้วหนึ่งสามารถใช้ฟังก์ชั่นที่รวดเร็วมากสำหรับการทำดัชนีให้ดูการดำเนินงานของฉันที่นี่ ฉันต้องการหารูปแบบการจัดทำดัชนีที่มีประสิทธิภาพสำหรับกรณีที่วงโคจรไม่จริง
=kl|ij=lk|ji=il|kj=kj|il

หมายเหตุ: ฟังก์ชั่นที่ฉันใช้จริงยอมรับตัวเลขสี่ตัวคือ , j , k , lในสัญกรณ์ที่เรียกว่า "วิชาเคมี" ( i j | k l ) = ฉันk | j l คืออาร์กิวเมนต์jและkนั้นถูกสับเปลี่ยนกัน แต่สิ่งนี้ไม่สำคัญผมJkล.(ผมJ|kล.)=ผมk|Jล.Jk


นอกหัวข้อ แต่ฉันเป็นเพียงคนเดียวที่ตกใจด้วยสัญกรณ์ ? ฆ่ามันฆ่ามันด้วยไฟ! d3x
n00b

1
เป็นเพียงทางลัดที่ใช้กันทั่วไปสำหรับ d x 1 d x 2 d x 3ที่ x = ( x 1 , x 2 , x 3 ) แทนที่จะเขียนมันออกมาอย่างชัดเจนมันง่ายกว่ามากที่จะใช้ d 3 xและทุกอย่างชัดเจน d3xdx1dx2dx3x=(x1,x2,x3)d3x
อองเดรเซอร์ติก

ฉันสามารถเห็นได้อย่างชัดเจนว่ามันคืออะไร: ทำให้เข้าใจผิดน่าเกลียดชังและต้องการการทำลายล้าง ไม่มีในx = ( x 1 , x 2 , x 3 )ดังนั้นทำไมจึงเป็นตัวแปรการรวม ทำไมไม่เพียงวันที่x ? ฉันรู้สึกไม่ดีที่มองมันและตอนนี้ฉันต้องนอนลง xx=(x1,x2,x3)dx
n00b

@ n00b ฉันคิดว่าเป็นที่ต้องการเพราะมันยังระบุมิติของอินทิกรัล (สำคัญมากเนื่องจากอินทิกรัลให้ผลลัพธ์ที่แตกต่างใน 1D, 2D และ 3D) d3x
OndřejČertík

คำตอบ:


5

[แก้ไข: เสน่ห์ครั้งที่ 4 ในที่สุดสิ่งที่เหมาะสม]

ฉันย้อนกลับไปที่นี้: ฉันเริ่มต้นด้วยคำตอบอื่นที่แสดงวิธีการกรองโดยใช้และใช้เพื่อสร้างชุดค่าผสมที่ถูกต้องทั้งหมดสำหรับชุดของค่าและดูลำดับบนฐานข้อมูลลำดับเลขจำนวนเต็มออนไลน์ จำนวนชุดค่าผสมคือn 2 ( n 2 + 3 )ซึ่งดูไม่น่าเป็นไปได้ (ทำไมต้องเป็น 3) นอกจากนี้ยังเป็นt ( t ( n ) ) + t ( t ( n - 1 ) )โดยที่t ( a )คือหมายเลขสามเหลี่ยมของann2(n2+3)เสื้อ(เสื้อ(n))+เสื้อ(เสื้อ(n-1))เสื้อ(a)a, 2 เมื่อได้รับสิ่งนี้เราต้องรู้ว่าทำไมเสื้อ(a)=a(a+1)/2

เทอมแรกนั้นง่ายกว่า - คู่ของคู่ที่และt i d ( i , j ) t ฉันd ( k , l ) , ที่t ฉันd ( a , b )คือดัชนีสามเหลี่ยมของa , . ฟังก์ชั่นแบบนี้:ผมJเสื้อผมd(ผม,J)เสื้อผมd(k,ล.)เสื้อผมd(a,)a,

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

ล.ล.k

เสื้อ(เสื้อ(n-1))

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)

ผมn3+Jn2+kn+ล.

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

สวัสดีฟิลขอบคุณมากสำหรับคำตอบ! ฉันทดสอบแล้วมีสองปัญหา ตัวอย่างเช่น idx_all (1, 2, 3, 4, 4) == idx_all (1, 2, 4, 3, 4) = 76. แต่ <12 | 34> / = <12 | 43> มันจะเท่ากันถ้าวงโคจรเป็นของจริง ดังนั้นวิธีแก้ปัญหาของคุณน่าจะเป็นในกรณีของ 8 symmetries (ดูตัวอย่าง Fortran ของฉันด้านบนสำหรับรุ่นที่ง่ายกว่านั่นคือ ijkl2intindex ()) ปัญหาที่สองคือการที่ดัชนีจะไม่ติดต่อกันผมวางผลลัพธ์ที่นี่: 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

<ผมJ|kล.> =-<Jผม|kล.> =-<ผมJ|ล.k> = <Jผม|ล.k>
ผมJkล.[ผมdxพีaผมR(ผมndอีxผมJ,ผมndอีxkล.,,)]sผมก.nผมJsผมก.nkล.

3

นี่คือแนวคิดของการใช้เส้นโค้งการเติมช่องว่างอย่างง่ายที่ปรับเปลี่ยนเพื่อส่งคืนคีย์เดียวกันสำหรับกรณีสมมาตร (ตัวอย่างโค้ดทั้งหมดเป็นไพ ธ อน)

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

หมายเหตุ:

  • ตัวอย่างคือไพ ธ อน แต่ถ้าคุณอินไลน์ฟังก์ชั่นเป็นรหัส fortran ของคุณและคลี่วงในสำหรับ (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)

คุณหมายถึง "if n คือพลังของ 2" แทนที่จะเป็น 2 เท่าหรือไม่
Aron Ahmadia

ใช่ขอบคุณ Aron ฉันเขียนคำตอบนี้ก่อนทานอาหารเย็นและ Hulk กำลังเขียน
fcruz

ฉลาด! อย่างไรก็ตามไม่ใช่ดัชนีสูงสุด n ^ 4 (หรือ n ^ 4-1 หากคุณเริ่มต้นจาก 0) ปัญหาคือสำหรับขนาดพื้นฐานที่ฉันต้องการจะทำก็ไม่พอดีกับหน่วยความจำ ด้วยดัชนีที่ต่อเนื่องกันขนาดของอาเรย์คือ n ^ 2 * (n ^ 2 + 3) / 4 อืมนั่นเป็นเพียงประมาณ 1/4 ของขนาดเต็มเลยล่ะค่ะ ดังนั้นฉันไม่ควรกังวลเกี่ยวกับปัจจัย 4 ในการใช้หน่วยความจำ ถึงแม้ว่าจะต้องมีวิธีเข้ารหัสดัชนีต่อเนื่องที่ถูกต้องโดยใช้สมมาตรทั้ง 4 เหล่านี้เท่านั้น (ดีกว่าวิธีแก้ปัญหาที่น่าเกลียดของฉันในโพสต์ซึ่งฉันต้องทำลูปสองครั้ง)
OndřejČertík

ใช่ถูกต้อง! ฉันไม่ทราบวิธีแก้ดัชนี (โดยไม่ต้องเรียงลำดับและจัดลำดับใหม่) อย่างหรูหรา แต่คำสำคัญที่ใช้ในการใช้หน่วยความจำคือ O (N ^ 4) ปัจจัย 4 ควรสร้างความแตกต่างเล็กน้อยในหน่วยความจำสำหรับ N ขนาดใหญ่
fcruz

0

นี่ไม่ใช่แค่ความเห็นทั่วไปของปัญหาการจัดทำดัชนีเมทริกซ์แบบสมมาตรหรือไม่? วิธีแก้ปัญหาที่มีออฟเซ็ต (i, j) = i * (i + 1) / 2 + j ใช่ไหม? คุณไม่สามารถทำสองสิ่งนี้และทำดัชนีอาเรย์ 4D สองเท่าได้หรือไม่? การใช้งานที่ต้องการการแตกกิ่งดูเหมือนไม่จำเป็น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.