ข้ามไปยังเนื้อหาหลัก
โครงการนี้อยู่ในช่วงเบต้า พบข้อผิดพลาด? สร้าง GitHub Issue มีคำถามหรือไอเดีย? ถามใน Discussions — ยินดีรับฟังความคิดเห็น!
คุณภาพการแปลในหน้านี้เป็นอย่างไร?

เพิ่มประสิทธิภาพการเตรียม Slater determinant

สำคัญ

แนวคิดในคู่มือนี้ใช้ได้เฉพาะใน Python API ในขณะนี้ ฟังก์ชันที่เทียบเท่ากันจะมีให้ใน C API ในรุ่นถัดไป

Gate InitializeModes ประกาศ occupation ของ mode อ้างอิง ส่วน Gate OrbitalRotation หมุนฐานอนุภาคเดี่ยว เมื่อวางต่อกัน Gate ทั้งสองจะเตรียม Slater determinant: mode เริ่มต้นใน determinant อ้างอิงแล้วถูกหมุนไปเป็นออร์บิทัลเป้าหมาย Gate PrepareSlaterDeterminant รวมการประกอบนั้นไว้ และเพราะมันมี occupation อ้างอิงอยู่ด้วย การ transpile มันจึงใช้ givens_decomposition_slater() แบบสี่เหลี่ยมผืนผ้าได้ ซึ่งทำเฉพาะออร์บิทัลที่ถูกครอบครอง แทนการแยกส่วนแบบสี่เหลี่ยมจัตุรัสเต็มรูปแบบที่ OrbitalRotation เพียงลำพังต้องใช้ นี่เป็นการสังเคราะห์ที่ถูกกว่าอย่างแท้จริง (ดูผลตอบแทนท้ายคู่มือนี้)

Transpiler pass MergeSlaterDeterminantPreparation ตรวจจับรูปแบบ InitializeModes ตามด้วย OrbitalRotation ใน FermionicCircuit และเขียนใหม่เป็น Gate PrepareSlaterDeterminant ขั้นตอนการสังเคราะห์ถัดไปจึงใช้การแยกส่วนที่ลดลงโดยอัตโนมัติ ในการจำลอง การเขียนใหม่นี้ไม่เปลี่ยนสถานะ: PrepareSlaterDeterminant เป็นแบบ validate-then-rotate (ตรวจสอบ occupation อ้างอิงแล้วจึงใช้การหมุน เหมือนกับ Gate สองตัวแยกกัน) การรวมจึงปลดล็อกการสังเคราะห์ที่ถูกกว่าโดยไม่เปลี่ยนสถานะที่เตรียมไว้

ตลอดคู่มือนี้ แต่ละ circuit จะถูกวาดก่อนและหลังรัน pass เคียงข้างกัน: อินพุตทางซ้าย และผลลัพธ์ของการรัน pass กับมันทางขวา มี helper สองตัวที่ทำเช่นนั้น merge รัน pass บนสำเนาของ circuit (ผ่าน FermionicDAGCircuit ของมัน) และแปลงผลลัพธ์กลับเป็น FermionicCircuit ที่วาดได้ และ draw_merge วาดทั้งสองครึ่งลงในภาพก่อน/หลังภาพเดียว:

>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import InitializeModes, OrbitalRotation
>>> from qiskit_fermions.transpiler import FermionicCircuitToDAG
>>> from qiskit_fermions.transpiler.converters import FermionicDAGToCircuit
>>> from qiskit_fermions.transpiler.passes import MergeSlaterDeterminantPreparation
>>>
>>> def merge(circuit):
... dag = FermionicCircuitToDAG().run(circuit)
... merged = MergeSlaterDeterminantPreparation().run(dag)
... return FermionicDAGToCircuit().run(merged)
>>>
>>> def natural_size(circuit):
... # the figure size Qiskit picks for a circuit on its own (discard the probe figure)
... probe = circuit.draw("mpl")
... size = probe.get_size_inches()
... plt.close(probe)
... return size
>>>
>>> def draw_merge(circuit):
... merged = merge(circuit)
... # let Qiskit size each half naturally, then lay them out side by side
... (bw, bh), (aw, ah) = natural_size(circuit), natural_size(merged)
... fig, (before, after) = plt.subplots(1, 2, figsize=(bw + aw, max(bh, ah)))
... circuit.draw("mpl", ax=before)
... merged.draw("mpl", ax=after)
... before.set_title("before")
... after.set_title("after")
... return fig

สิ่งที่ถูกรวม​

pass จดจำรูปแบบสามแบบ ทั้งหมดอิงกับข้อตกลง mode แบบบล็อกสปินที่ InitializeModes และ OrbitalRotation ใช้ สำหรับระบบที่มีออร์บิทัลเชิงพื้นที่ norb ตัว mode 0..norb คือเซกเตอร์สปินแอลฟา และ norb..2*norb คือเซกเตอร์สปินเบตา การเตรียม Slater determinant ทำแยกตามเซกเตอร์สปิน เพราะแต่ละเซกเตอร์มีรูปร่าง (อิเล็กตรอน, ออร์บิทัล) ของตัวเอง

รีจิสเตอร์เต็ม (หรือไม่มีสปิน)​

รูปแบบที่ง่ายที่สุดคือใช้ InitializeModes ตามด้วย OrbitalRotation ทันทีบน mode ชุดเดียวกัน นี่คือกรณีไม่มีสปิน (determinant เดียวเหนือทุก mode) และรวมถึง circuit ที่มีสปินซึ่งการหมุนครอบคลุมทั้งสองเซกเตอร์พร้อมกัน Gate ทั้งสองหลอมรวมเป็น PrepareSlaterDeterminant ตัวเดียว:

>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A mode initialization and rotation fusing into one Slater determinant preparation.

แยกตามเซกเตอร์สปิน​

รูปร่างเดียวกัน แต่จำกัดอยู่ที่สปินครึ่งเดียว ในที่นี้ คู่ initialize-แล้ว-rotate อิสระสองคู่ คู่ละหนึ่งเซกเตอร์ จะหลอมรวมกันเองเป็น PrepareSlaterDeterminant:

>>> circuit = FermionicCircuit(6) # norb = 3: alpha modes 0..3, beta modes 3..6
>>> circuit.append(InitializeModes([1, 1, 0]), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(InitializeModes([1, 0, 0]), circuit.modes[3:])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

Two per-sector initialize-and-rotate pairs, each fusing into its own preparation.

การเริ่มต้นแบบรวม การหมุนแยกตามสปิน​

แบบนี้ประกอบด้วย InitializeModes แบบรีจิสเตอร์เต็ม (2 * norb) หนึ่งตัว ตามด้วย action OrbitalRotation สอง ตัว ตัวละหนึ่งบนสปินครึ่งที่ต่อเนื่องกันแต่ละข้าง pass แยก occupation รวมที่ขอบเขตเซกเตอร์ และสร้าง Gate PrepareSlaterDeterminant สอง ตัว การหมุนทั้งสองอาจปรากฏในลำดับใดก็ได้

นี่คือรูปร่างที่เวิร์กโฟลว์ local unitary cluster Jastrow (LUCJ) สร้างขึ้น (ดู คู่มือ LUCJ) ผู้ใช้วาง InitializeModes (โดยทั่วไปคือ reference แบบ Hartree-Fock โดยใช้ from_hartree_fock()) ไว้หน้าสุดของ circuit แล้วต่อ ansatz UCJ ซึ่งการหมุนออร์บิทัลแยกตามสปินสองตัวแรกต่อจากการเริ่มต้นโดยตรงเมื่อ ansatz ถูก decompose

>>> circuit = FermionicCircuit(6) # norb = 3, nelec = (2, 1)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[3:])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with two per-spin rotations fusing into two preparations.

รูปแบบนี้ทำงานเช่นกันเมื่อมีสปินครึ่งเดียวเพียงข้างเดียวที่ถูกหมุนด้วย Gate ที่ตามหลังการเริ่มต้นโดยตรง ซึ่งเป็นรูปร่างที่เกิดเมื่อการหมุนออร์บิทัลของเซกเตอร์สปินเพียงเซกเตอร์เดียวบังเอิญอยู่หน้าสุดของ circuit ครึ่งที่ถูกหมุนจะกลายเป็น PrepareSlaterDeterminant พร้อมการหมุนของมัน และอีกครึ่งถูกเตรียมด้วยการหมุนแบบ เอกลักษณ์ การเตรียมแบบเอกลักษณ์สังเคราะห์เป็น X Gate อ้างอิงที่ InitializeModes จะปล่อยออกมาสำหรับครึ่งนั้นอยู่แล้ว การเติมจึงไม่เพิ่ม Gate ใด ๆ แต่ยังปลดล็อกการสังเคราะห์ Slater แบบลดรูปบนครึ่งที่ถูกหมุน:

>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes.from_hartree_fock(3, (2, 1)), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A global initialization with a single per-spin rotation fusing into two preparations.

สิ่งที่ไม่ถูกแตะต้อง​

pass นี้อนุรักษ์นิยมโดยเจตนา มันหลอมรวมเฉพาะเมื่อ Gate ทั้งสองประกอบเป็นการเตรียม Slater determinant จริง ๆ และคัดลอกทุกอย่างที่เหลือผ่านไปโดยไม่เปลี่ยนแปลง “ตามทันที” หมายถึงตามกราฟการไหลของข้อมูลของ circuit OrbitalRotation หลอมรวมเฉพาะเมื่อ InitializeModes เป็นตัวก่อนหน้าตัวเดียวของมันตลอดทุก mode ในรูปด้านล่าง ก่อนและหลังเหมือนกัน: ไม่มีอะไรถูกหลอมรวม

การดำเนินการคั่นกลาง​

หากมี Gate ใดแตะ mode ระหว่างการเริ่มต้นและการหมุน Gate เหล่านั้นจะไม่ประกอบเป็นการเตรียมล้วนอีกต่อไป จึงไม่มีอะไรถูกรวม:

>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> circuit = FermionicCircuit(4)
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> number_op = FermionOperator.from_dict({(cre(0), ann(0)): 1.0})
>>> circuit.append(Evolution(4, number_op, time=0.5), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(4)), circuit.modes)
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

An operation between the initialization and rotation blocks the merge.

การหมุนบางส่วนที่ไม่ใช่สปินครึ่ง​

การหมุนที่ครอบคลุมสปินครึ่งที่ต่อเนื่องกันครึ่งเดียวพอดีของการเริ่มต้นแบบรีจิสเตอร์เต็ม จะหลอมรวม (ดูหัวข้อก่อนหน้า) แต่การหมุนบนเซตของ mode บางส่วนอื่น (น้อยกว่าจำนวน mode ของการเริ่มต้น และไม่ใช่สปินครึ่งทั้งสองข้าง เช่น ช่วงย่อยที่คร่อมขอบเขตเซกเตอร์) ไม่ใช่การเตรียมแยกตามเซกเตอร์ การรวมมันจะทำให้บทบาทของการเริ่มต้นบน mode ที่ถูกละไว้หายไปอย่างเงียบ ๆ ดังนั้น pass จึงปล่อย Gate ทั้งสองไว้ตามเดิม:

>>> circuit = FermionicCircuit(4) # norb = 2: the only spin halves are modes [0, 1] and [2, 3]
>>> circuit.append(InitializeModes([1, 1, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(np.eye(3)), circuit.modes[:3])
>>> draw_merge(circuit)
<Figure size ... with 2 Axes>

A partial rotation that is not a spin half is left unmerged.

ทำไมจึงสำคัญ: ผลตอบแทนของการสังเคราะห์​

การรวมคุ้มค่าเพราะ PrepareSlaterDeterminant สังเคราะห์เป็น Gate น้อยกว่ามากเมื่อเทียบกับ OrbitalRotation ที่มันดูดซับไว้ determinant ที่มีอิเล็กตรอน mm ตัวเหนือออร์บิทัล nn ตัวต้องการการหมุนสอง qubit มากที่สุด m(n−m)m(n-m) ครั้งและไม่มี Gate เฟสแนวทแยง เทียบกับการหมุน n(n−1)/2n(n-1)/2 ครั้งบวกเฟส nn ตัวของการหมุนออร์บิทัลแบบสี่เหลี่ยมจัตุรัสเต็มรูปแบบ

pass manager preset แบบ Jordan-Wigner รัน MergeSlaterDeterminantPreparation ในขั้นตอนการปรับให้เหมาะสม การลดนี้จึงเกิดขึ้นโดยอัตโนมัติ ลอง transpile การเตรียมแบบสองอิเล็กตรอนหกออร์บิทัลจนถึง QuantumCircuit แล้วนับ Gate ของมัน:

หมายเหตุ

preset รัน MergeOrbitalRotations ก่อน MergeSlaterDeterminantPreparation ทันที ซึ่งสำคัญเมื่อมี ชุด Gate OrbitalRotation ที่ต่อเนื่องกันตามหลังการเริ่มต้น pass ก่อนหน้าจะยุบชุดนั้นเป็นการหมุนเดียวก่อน จากนั้น MergeSlaterDeterminantPreparation จึงหดมันเข้ากับการเริ่มต้นเป็น PrepareSlaterDeterminant ตัวเดียว หากไม่มีลำดับนั้น จะดูดซับได้เฉพาะการหมุนแรกของชุด และที่เหลือจะถูกสังเคราะห์ด้วยการแยกส่วนแบบสี่เหลี่ยมจัตุรัสเต็มรูปแบบ

>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> # a reproducible random 6 x 6 orbital rotation
>>> rng = np.random.default_rng(1)
>>> z = rng.standard_normal((6, 6)) + 1j * rng.standard_normal((6, 6))
>>> q, r = np.linalg.qr(z)
>>> rotation = q * (np.diagonal(r) / np.abs(np.diagonal(r)))
>>>
>>> circuit = FermionicCircuit(6)
>>> circuit.append(InitializeModes([1, 1, 0, 0, 0, 0]), circuit.modes)
>>> circuit.append(OrbitalRotation(rotation), circuit.modes)
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> prepared = pm.run(circuit)
>>> print(dict(sorted(prepared.count_ops().items())))
{'x': 2, 'xx_plus_yy': 8}
>>> prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized merged Slater determinant preparation.

เทียบกับการสังเคราะห์การหมุนออร์บิทัลเดียวกันโดยลำพัง เมื่อไม่มีการเริ่มต้นก่อนหน้า ก็ไม่มีอะไรให้รวม preset จึงถอยกลับไปใช้การแยกส่วนแบบสี่เหลี่ยมจัตุรัสเต็มรูปแบบ ซึ่งลึกกว่าและมี Gate เฟสแนวทแยง:

>>> reference = FermionicCircuit(6)
>>> reference.append(OrbitalRotation(rotation), reference.modes)
>>> reference_prepared = pm.run(reference)
>>> print(dict(sorted(reference_prepared.count_ops().items())))
{'rz': 6, 'xx_plus_yy': 15}
>>> reference_prepared.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

The synthesized bare orbital rotation, with its extra phase gates.

การแยกส่วนที่ลดลงตัด Gate เฟสทั้งหกออก และลดจำนวนการหมุนสอง qubit ลงเกือบครึ่ง determinant ที่เตรียมได้ถูกต้องจนถึงเฟสรวม (ไม่มีนัยสำคัญทางกายภาพสำหรับการเตรียมสถานะ) ซึ่งเป็นสิ่งที่ Gate เฟสจะแก้ไขไว้

สองวิธีในการแยก Gate​

PrepareSlaterDeterminant แยกส่วนได้สองวิธีที่ต่างกัน และใช้แทนกันไม่ได้:

decompose()​

ขยาย Gate เป็นนิยามของมัน: InitializeModes ของ reference ตามด้วย OrbitalRotation เต็มรูปแบบ ซึ่งเทียบเท่ากับ Gate ในฐานะตัวดำเนินการ สำหรับสถานะอินพุตใด ๆ

PrepareSlaterDeterminant.synthesis ซึ่งถูกใช้งานโดย F2FSynthesis​

แทนที่ Gate ด้วยการแยกส่วนที่ลดลงของคู่มือนี้ ซึ่งเทียบเท่ากับ Gate เฉพาะในฐานะการเตรียมสถานะ: มันสร้าง determinant เดียวกัน จนถึงเฟสรวม เมื่อ mode เริ่มต้นในสุญญากาศ มันถูกกว่าก็เพราะสมมติเช่นนั้นนั่นเอง

ใช้การสังเคราะห์เมื่อ Gate เตรียมสถานะบนสุญญากาศ ซึ่งเป็นจุดประสงค์ของ Gate และเป็นสิ่งที่ transpiler ทำให้คุณ ใช้ decompose() เมื่อ Gate ต้องยังคงทำหน้าที่เป็นตัวดำเนินการ เช่น เพราะ Gate อื่นอยู่ก่อนหน้ามันบน mode เดียวกัน หรือเมื่อคุณต้องการตรวจสอบหรือจำลอง Gate ธรรมดา pass F2FSynthesis ไม่อาจรู้ว่ากำลังเจอกรณีใด จึงขึ้นอยู่กับ circuit ที่คุณให้ว่าจะเตรียม determinant จากสุญญากาศ

การจำลอง circuit ด้วย ffsim ไม่ต้องใช้ทั้งสองอย่าง: ทุก Gate ใช้ยูนิทารีทั้งหมดของมัน ดังนั้น circuit ที่มี PrepareSlaterDeterminant จึงถูกจำลองตามที่เป็น และอยู่บนเส้นทางเร็วสำหรับ Gate OrbitalRotation รอบ ๆ มัน การแยกเฉพาะ Slater gate ด้วย circuit.decompose("PrepareSlaterDeterminant") ก็ทำให้การหมุนเหล่านั้นยังคงครบเช่นกัน

ดูเพิ่มเติม

PrepareSlaterDeterminant, InitializeModes, OrbitalRotation, MergeOrbitalRotations, F2FSynthesis, FermionicSlaterGivensDecomposition และ คู่มือ LUCJ สำหรับเวิร์กโฟลว์ที่สร้างรูปแบบการเริ่มต้นแบบรวม