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

สังเคราะห์ Evolution gate ในปริภูมิเฟอร์มิออน

สำคัญ

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

Evolution gate แทนวิวัฒนาการตามเวลา e−itHe^{-itH} ของทั้งตัวดำเนินการ การสังเคราะห์จากเฟอร์มิออนเป็นเฟอร์มิออนจะเขียน gate นั้นใหม่เป็น FermionicCircuit ของ fermionic gate ที่เล็กลง ทั้งสองฝั่งของการเขียนใหม่ยังอยู่ในปริภูมิเฟอร์มิออน ซึ่งเป็นสิ่งที่ทำให้ต่างจากการสังเคราะห์จากเฟอร์มิออนเป็น qubit ที่ตามมา: groups ของตัวดำเนินการ (ดู จัดกลุ่มเทอมของตัวดำเนินการ: ใช้โครงสร้างของตัวดำเนินการ) ยังคงอยู่ ในขณะที่การแมปจากเฟอร์มิออนเป็น qubit คืนค่าเป็น SparseObservable ซึ่งไม่มีแนวคิดเรื่องการจัดกลุ่ม

มีตัวเลือกสองอย่างสำหรับเลือกวิธี Evolution.synthesis กำหนดวิธีให้ gate ตัวเดียว และ transpiler pass F2FSynthesis กำหนดให้ทั้ง circuit ทั้งสองรับ F2FSynthesisPlugin ซึ่งเป็นอินเทอร์เฟซที่การเขียนใหม่ลักษณะนี้ทุกแบบ implement วิธีที่แพ็กเกจนี้มีให้สำหรับ evolution ในตอนนี้เป็นสูตรผลคูณ (product formula) ซึ่งเป็นสิ่งที่ส่วนที่เหลือของคู่มือนี้ใช้: FermionicLieTrotter สำหรับสูตรอันดับหนึ่ง (ค่าเริ่มต้น) และ FermionicSuzukiTrotter สำหรับอันดับคู่ที่สูงกว่า สิ่งอื่นใดที่ implement อินเทอร์เฟซนี้เลือกใช้ได้ในแบบเดียวกัน และหัวข้อเกี่ยวกับตัวเลือกทั้งสองใช้ได้กับมันโดยไม่เปลี่ยนแปลง

หมายเหตุ

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

ทั้งสองขั้นตอนประกอบกันได้ ดังนั้น evolution อาจถูกประมาณที่ระดับใดระดับหนึ่ง ทั้งสองระดับ หรือไม่ประมาณเลยก็ได้ สูตรผลคูณเป็นการประมาณ เว้นแต่ตัวประกอบของมันสลับที่กันได้ ส่วนวิธีการสังเคราะห์ไม่จำเป็นต้องประมาณเลย

หมายเหตุ

การข้ามขั้นตอนนี้ไม่จำเป็นต้องหมายความว่าสูญเสียการจัดกลุ่มไปทั้งหมด การห่อ mapper ด้วย group_wise() จะแมปตัวดำเนินการทีละกลุ่มแล้วรวมผลลัพธ์ ทำให้เทอม Pauli ที่แมปแล้วมาเป็นกลุ่มแทนที่จะสลับปนกัน สูตรผลคูณฝั่ง qubit จะสังเคราะห์ตามลำดับที่ได้รับ ดังนั้นเทอมที่สลับที่กันได้และอยู่ติดกันจึงจัดตารางพร้อมกันได้

บนแฮมิลโทเนียนด้านล่าง วิธีนี้ได้ประโยชน์ด้านความลึกของ two-qubit ครบถ้วนเท่ากับการแบ่ง: 18 แทนที่จะเป็น 30 โดยจำนวน gate ไม่เปลี่ยน ความต่างอยู่ที่ ตำแหน่ง ที่โครงสร้างถูกแสดงออก การแบ่งในปริภูมิเฟอร์มิออนทำให้แต่ละกลุ่มเป็น gate แยกกัน ซึ่งทุกขั้นตอนหลังมองเห็นและดำเนินการได้ ส่วน group_wise เหลือ gate เดียวและจัดลำดับเทอมภายในมันใหม่เท่านั้น ซึ่งพอสำหรับการจัดตาราง แต่ไม่เหลืออะไรให้ fermionic pass ทำงานด้วย ใช้วิธีนี้เมื่อ evolution ต้องเป็น gate เดียว และใช้สูตรผลคูณเฟอร์มิออนในกรณีอื่น

สร้างแฮมิลโทเนียนที่จัดกลุ่ม​

สูตรผลคูณทั้งสองแบ่งตัวดำเนินการเป็นตัวประกอบ และสำหรับตัวดำเนินการที่จัดกลุ่ม ตัวประกอบเหล่านั้นคือกลุ่มของมัน การจัดกลุ่มจึงเป็นตัวกำหนดทั้งต้นทุนและความแม่นยำของผลลัพธ์ ลองใช้โซ่ Fermi-Hubbard หกไซต์ที่จัดกลุ่มตามการสลับที่ของเทอม: เทอม hopping บนพันธะคู่ เทอม hopping บนพันธะคี่ และอันตรกิริยาแนวทแยง

>>> from qiskit_fermions.operators import FermionOperator, cre, ann
>>>
>>> def fermi_hubbard_chain(num_sites, tunneling, interaction):
... """A 1D Fermi-Hubbard chain grouped into even bonds, odd bonds, and the interaction."""
... terms, groups = [], []
... for offset in (0, 1):
... for j in range(offset, num_sites - 1, 2):
... terms += [
... ([cre(j), ann(j + 1)], -tunneling),
... ([cre(j + 1), ann(j)], -tunneling),
... ]
... groups += [offset, offset]
... for j in range(num_sites - 1):
... terms.append(([cre(j), ann(j), cre(j + 1), ann(j + 1)], interaction))
... groups.append(2)
... operator = FermionOperator.from_terms(terms)
... operator.groups = groups
... return operator
>>>
>>> num_sites = 6
>>> hamiltonian = fermi_hubbard_chain(num_sites, tunneling=1.0, interaction=2.0)
>>> hamiltonian.num_groups()
3

สิบห้าเทอมใน 3 กลุ่ม แต่ละกลุ่ม hopping จับคู่ทุกเทอมกับคู่สังยุคของมัน ซึ่งสำคัญ: ตัวประกอบต้องเป็นเฮอร์มิเชียนเพื่อให้เอกซ์โพเนนเชียลของมันเป็นยูนิทารี และเรื่องนี้ไม่ได้ตามมาจากการที่ผลรวมเป็นเฮอร์มิเชียน groups_are_hermitian() ตรวจสอบข้อกำหนดนี้แทนที่จะปล่อยให้ตรวจด้วยตา:

>>> from qiskit_fermions.operators.terms.grouping import groups_are_hermitian
>>>
>>> groups_are_hermitian(hamiltonian)
[True, True, True]

การแยก a0†a1a^\dagger_0 a_1 ออกจากคู่ของมัน a1†a0a^\dagger_1 a_0 จะทำให้ข้อกำหนดนี้เสีย และการปล่อยตัวดำเนินการไม่จัดกลุ่มก็มีผลเหมือนกัน เพราะทั้งสองสูตรจะแบ่งมัน ทีละเทอม และ ai†aja^\dagger_i a_j เดี่ยว ๆ ไม่เป็นเฮอร์มิเชียน:

>>> ungrouped = fermi_hubbard_chain(num_sites, tunneling=1.0, interaction=2.0)
>>> ungrouped.groups = range(len(list(ungrouped.iter_terms()))) # one group per term
>>> all(groups_are_hermitian(ungrouped))
False

ตัวประกอบเช่นนี้เป็นตัวดำเนินการเฟอร์มิออนที่ถูกต้อง และแพ็กเกจนี้จะสร้าง gate ให้ จึงไม่มีอะไรแจ้งเตือนจนกว่าตัวดำเนินการที่แมปแล้วจะถึง PauliEvolutionGate ซึ่งต้องการสัมประสิทธิ์จริงและจะ raise ValueError: Operator contains complex coefficients, which are not supported การตรวจสอบการจัดกลุ่มล่วงหน้าเปลี่ยนความล้มเหลวที่มาช้านั้นให้เป็นคำตอบที่ได้ก่อนสร้าง circuit ใด ๆ

กำหนดวิธีบน gate หนึ่งตัว​

ส่งวิธีการสังเคราะห์ให้ gate แล้ว decompose แต่ละ gate ที่มันสร้างออกมาจะกลับมาเป็น Evolution gate ของตัวเอง:

>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.circuit.library.synthesis import (
... FermionicLieTrotter,
... FermionicSuzukiTrotter,
... )
>>>
>>> total_time = 1.0
>>>
>>> def factors(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, hamiltonian, time=total_time, synthesis=synthesis),
... circuit.modes,
... )
... return circuit.decompose().count_ops()["Evolution"]
>>>
>>> factors(FermionicLieTrotter())
3
>>> factors(FermionicSuzukiTrotter(order=2))
5
>>> factors(FermionicSuzukiTrotter(order=2, reps=4))
17
>>> factors(FermionicSuzukiTrotter(order=2, reps=4, merge_adjacent=False))
20

อันดับหนึ่งสร้างหนึ่งตัวประกอบต่อกลุ่ม อันดับสองสร้างพาลินโดรมสมมาตร ดังนั้นสองกลุ่มนอกสุดปรากฏสองครั้ง ครั้งละครึ่งเวลา รวมเป็นห้า การทำซ้ำสี่ครั้งจะได้ยี่สิบ แต่แต่ละรอบจบด้วยกลุ่มเดียวกับที่รอบถัดไปเริ่ม gate ที่อยู่ติดกันเหล่านั้นวิวัฒนาการภายใต้ตัวดำเนินการเดียวกัน จึงถูกรวมเป็น gate เดียวด้วยเวลารวม ซึ่งประหยัดหนึ่ง gate ต่อรอยต่อ ส่ง merge_adjacent=False เพื่อคงลำดับตามตำรา ทุกอันดับแบ่งตัวดำเนินการแบบเดียวกัน สิ่งที่เปลี่ยนคือลำดับของตัวประกอบและเวลาวิวัฒนาการของมัน ไม่ใช่การแบ่งส่วน

หมายเหตุ

ตัวประกอบถูกทำเครื่องหมายเป็น atomic ดังนั้นการ decompose ซ้ำจะปล่อยไว้ที่เดิมแทนที่จะแบ่งต่อ การ decompose มีจุดตรึง ซึ่งทำให้เรียก decompose() ซ้ำได้อย่างปลอดภัย

กำหนดวิธีสำหรับทั้งไปป์ไลน์​

การกำหนด synthesis ต่อ gate หมายถึงต้องส่งต่อตัวเลือกผ่านทุกสิ่งที่สร้าง gate รวมถึง UCC และ UCJ ซึ่งสร้าง gate ของมันภายใน pass F2FSynthesis ใช้วิธีเดียวกับทุก Evolution ใน circuit แทน ดังนั้นเลือกครั้งเดียวสำหรับทั้งไปป์ไลน์ ตั้งค่าตามชื่อ gate เช่นเดียวกับ F2QSynthesis และ gate ที่ไม่ได้ระบุชื่อจะถูกปล่อยไว้ตามเดิม:

>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.passes import F2FSynthesis
>>>
>>> circuit = FermionicCircuit(num_sites)
>>> circuit.append(Evolution(num_sites, hamiltonian, time=total_time), circuit.modes)
>>>
>>> expanded = FermionicPassManager(
... F2FSynthesis({"Evolution": ("SuzukiTrotter", (), {"order": 2, "reps": 4})})
... ).run(circuit)
>>> expanded.count_ops()["Evolution"]
17

นี่คือจำนวนเดียวกับที่ decompose() ให้สำหรับวิธีนั้นข้างต้น pass MergeEvolutions ใช้การรวมแบบเดียวกันกับ gate ข้างเคียงที่ไม่ได้มาจากการสังเคราะห์เดียวกัน เช่นรอยต่อระหว่าง circuit ที่ต่อกัน pass ทำสูตรเองทั้งหมด จึงไม่ต้องมีขั้นตอนขยายแยกต่างหาก และรันซ้ำสองครั้งก็ไม่เปลี่ยนอะไร เพราะตัวประกอบที่มันสร้างเป็น atomic จึงไม่มีนิยามของตัวเอง

หมายเหตุ

ส่ง apply=False เพื่อ เลือก วิธีเท่านั้น แล้วปล่อยการขยายให้สิ่งอื่นทำ เช่น Decompose ของ Qiskit (การระบุชื่อ gate เป็น Decompose("Evolution") จำกัดเฉพาะ evolution และปล่อยให้ fermionic gate อื่นใช้ synthesis plugin ของตัวเอง)

วิธีนี้มีประโยชน์เมื่อต้องการเลือกวิธีสำหรับทั้ง circuit ไว้ก่อน แล้วเลื่อนการขยายไปยังจุดที่ต้องการ เช่นเพื่อตรวจสอบ circuit ก่อน:

>>> selected = FermionicPassManager(
... F2FSynthesis({"Evolution": ("SuzukiTrotter", (), {"order": 2})}, apply=False)
... ).run(circuit)
>>> selected.count_ops()["Evolution"] # selected, not yet expanded
1
>>> selected.decompose().count_ops()["Evolution"] # expanded on demand
5

แต่ต้องระวังว่า gate ที่ไม่ได้ขยายทำอะไร: มันไปถึงขั้นตอนจากเฟอร์มิออนเป็น qubit ทั้งตัว ซึ่งถูกแมปโดยไม่ดู synthesis นี่ทำให้พลาดง่ายเมื่อเปรียบเทียบวิธี เพราะทุกวิธีให้ผลลัพธ์เหมือนกัน และการเปรียบเทียบจึงไม่ได้วัดอะไรเลยโดยไม่มีสัญญาณเตือน บนแฮมิลโทเนียนข้างต้น การปล่อย gate อันดับสองไม่ขยายทำให้ circuit ที่ใหญ่กว่ามากด้านล่างยุบเหลือต้นทุนของ evolution ที่แมปแล้วหนึ่งตัว ไม่ว่าจะเลือกวิธีใด

วัดผลตอบแทน​

อันดับที่สูงกว่าต้องแลกกับความลึก ดังนั้นคำถามไม่ใช่ว่าแม่นยำกว่าหรือไม่ แต่คือแม่นยำกว่า ต่อ gate หรือไม่ ต้องใช้ตัวเลขสองตัวต่อวิธี: ความคลาดเคลื่อนของสถานะที่ได้ และต้นทุน two-qubit ของ circuit ที่สร้างมัน

ความคลาดเคลื่อนวัดด้วย ffsim ซึ่งจำลอง FermionicCircuit โดยตรง (ดู แพ็กเกจนี้เกี่ยวข้องกับ ffsim อย่างไร) ไม่มีการแมปจากเฟอร์มิออนเป็น qubit เข้ามาเกี่ยวข้อง การเปรียบเทียบจึงแยกสูตรผลคูณเฟอร์มิออนออกจากสิ่งที่การแมปหรือการสังเคราะห์ฝั่ง qubit อาจมีส่วน มันยังทำงานในเซกเตอร์จำนวนอนุภาคคงที่ ซึ่งทั้งเล็กกว่าปริภูมิเต็ม 2N2^N และเป็นปริภูมิที่ evolution อยู่จริง

>>> import ffsim
>>> import numpy as np
>>> from scipy.sparse.linalg import expm_multiply
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> norb, nelec = num_sites, (3, 0) # three spinless fermions on six modes
>>> initial = ffsim.hartree_fock_state(norb, nelec)
>>>
>>> hamiltonian_operator = ffsim.linear_operator(hamiltonian, norb=norb, nelec=nelec)
>>> exact = expm_multiply(
... -1j * total_time * hamiltonian_operator,
... initial,
... traceA=-1j * total_time * ffsim.trace(hamiltonian, norb=norb, nelec=nelec),
... )
>>>
>>> def cost_and_error(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, hamiltonian, time=total_time, synthesis=synthesis),
... circuit.modes,
... )
... # the method is set on the gate, so decompose() is what expands it here; a pipeline
... # would reach for F2FSynthesis instead (see the note above)
... circuit = circuit.decompose()
... evolved = ffsim.apply_unitary(initial, circuit, norb=norb, nelec=nelec)
... error = 1 - abs(np.vdot(evolved, exact)) ** 2
... qubit_circuit = generate_preset_jw_pass_manager().run(circuit).decompose(reps=6)
... two_qubit = lambda instruction: len(instruction.qubits) == 2
... return error, qubit_circuit.count_ops()["cx"], qubit_circuit.depth(two_qubit)
>>>
>>> methods = [
... ("order 1, reps 1 ", FermionicLieTrotter()),
... ("order 2, reps 1 ", FermionicSuzukiTrotter(order=2)),
... ("order 1, reps 10", FermionicLieTrotter(reps=10)),
... ("order 2, reps 4 ", FermionicSuzukiTrotter(order=2, reps=4)),
... ]
>>> costs = {label: cost_and_error(synthesis) for label, synthesis in methods}
>>> labels, counts, depths = [], [], []
>>> for label, (error, gates, depth) in costs.items():
... labels.append(label.strip().replace(", ", "\n"))
... counts.append(gates)
... depths.append(depth)
... print(f"{label} error {error:.6f}")
order 1, reps 1 error 0.515997
order 2, reps 1 error 0.180958
order 1, reps 10 error 0.004026
order 2, reps 4 error 0.000421

ต้นทุน two-qubit ถูกเว้นจากตารางนั้นโดยตั้งใจ มันขึ้นกับการสังเคราะห์ฝั่ง qubit ที่ Qiskit เลือก ซึ่งดีขึ้นในแต่ละรุ่น ตัวเลขที่พิมพ์ที่นี่จึงเป็นเพียงภาพรวม ณ เวลาหนึ่ง ไม่ใช่คุณสมบัติของสูตรผลคูณ ภาพด้านล่างพล็อตตัวเลขเหล่านั้นแทน และสิ่งที่การเปรียบเทียบตั้งอยู่บนนั้นถูกยืนยันเป็นความสัมพันธ์:

Grouped bars of CX count and depth per method, with order two at four repetitions clearly shorter than order one at ten.

อ่านสองวิธีสุดท้ายด้วยกัน เพราะเป็นการเปรียบเทียบที่สำคัญ อันดับสองกับสี่รอบแม่นยำกว่าอันดับหนึ่งกับสิบรอบราวสิบเท่า และทำได้ด้วย entangling gate น้อยกว่า ไม่ใช่มากกว่า ตามที่ภาพแสดง การใช้งบประมาณ gate กับอันดับที่สูงกว่าชนะการใช้กับขั้นอันดับหนึ่งที่มากขึ้นในกรณีนี้

ผลลัพธ์นั้นไม่เป็นสากล การเพิ่ม reps และการเพิ่ม order ลดความคลาดเคลื่อนในอัตราต่างกันสำหรับต้นทุนประเภทเดียวกัน และอย่างไหนชนะขึ้นอยู่กับตัวดำเนินการและเวลาที่ใช้วิวัฒนาการ สองแถวข้างต้นคือวิธีค้นหาคำตอบสำหรับปัญหาหนึ่ง ๆ

หมายเหตุ

สูตรเฟอร์มิออนประกอบกับสูตรที่เลือกสำหรับขั้นตอนจากเฟอร์มิออนเป็น qubit (ดู MapperFnEvolutionSynthesis.product_formula) เมื่อทั้งสองเป็นการประมาณ ความแม่นยำของผลลัพธ์ถูกกำหนดโดยตัวที่อ่อนกว่า ดังนั้นการเพิ่มอันดับที่นี่ในขณะที่สูตรฝั่ง qubit ยังเป็นอันดับหนึ่งจึงได้ประโยชน์น้อย

จำกัด pass ให้ใช้กับ gate ที่ได้ประโยชน์​

อันดับที่สูงกว่าช่วยได้เฉพาะเมื่อตัวประกอบไม่สลับที่กัน หากทั้งหมดสลับที่กันได้ สูตรอันดับหนึ่งก็แม่นยำอยู่แล้ว และตัวประกอบเพิ่มเป็นภาระเปล่า ๆ ตัวดำเนินการแนวทแยงล้วนทำให้เห็นชัด เพราะตัวดำเนินการจำนวนสลับที่กันเอง:

>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> diagonal = FermionOperator.from_terms(
... [([cre(j), ann(j), cre(j + 1), ann(j + 1)], 1.0 + 0.1 * j) for j in range(num_sites - 1)]
... )
>>> diagonal.groups = list(range(num_sites - 1))
>>>
>>> def diagonal_cost(synthesis):
... circuit = FermionicCircuit(num_sites)
... circuit.append(
... Evolution(num_sites, diagonal, time=total_time, synthesis=synthesis), circuit.modes
... )
... transpiled = generate_preset_jw_pass_manager().run(circuit.decompose())
... return transpiled.decompose(reps=6).count_ops()["cx"]
>>>
>>> diagonal_cost(FermionicLieTrotter())
10
>>> diagonal_cost(FermionicSuzukiTrotter(order=2))
18

ทั้งสอง circuit แม่นยำ ดังนั้นตัวที่สองใช้ entangling gate มากกว่า 80% โดยเปล่าประโยชน์ ตัวดำเนินการ Coulomb แนวทแยงของ UCJ มีรูปแบบนี้ ซึ่งเป็นเหตุผลที่ F2FSynthesis.filter มีอยู่: มันรับ predicate บน DAGOpNode และปล่อย gate ที่ถูกปฏิเสธไว้ตามเดิม ดังนั้น pass เดียวจึงเพิ่มอันดับให้ gate ที่ได้ประโยชน์และปล่อยที่เหลือไว้ที่อันดับหนึ่ง

circuit ที่มีทั้งสองชนิดของ evolution แสดงให้เห็นว่าประหยัดได้เท่าไร predicate ด้านล่างรับ gate เฉพาะเมื่อตัวดำเนินการของมันย้ายอนุภาคระหว่างโหมด ซึ่งเป็นกรณีที่กลุ่มไม่สลับที่กันพอดี และอันดับที่สูงกว่ามีอะไรให้ได้

>>> def is_diagonal(operator):
... """Whether every term only counts particles, rather than moving them between modes."""
... return all(
... sorted(mode for created, mode in actions if created)
... == sorted(mode for created, mode in actions if not created)
... for actions, _ in operator.iter_terms()
... )
>>>
>>> def mixed_circuit():
... circuit = FermionicCircuit(num_sites)
... circuit.append(Evolution(num_sites, hamiltonian, time=total_time), circuit.modes)
... circuit.append(Evolution(num_sites, diagonal, time=total_time), circuit.modes)
... return circuit
>>>
>>> def two_qubit_cost(circuit):
... transpiled = generate_preset_jw_pass_manager().run(circuit).decompose(reps=6)
... two_qubit = lambda instruction: len(instruction.qubits) == 2
... return transpiled.count_ops()["cx"], transpiled.depth(two_qubit)
>>>
>>> order_2 = FermionicSuzukiTrotter(order=2)
>>>
>>> everywhere = two_qubit_cost(
... FermionicPassManager(F2FSynthesis({"Evolution": order_2})).run(mixed_circuit())
... )
>>> restricted = two_qubit_cost(
... FermionicPassManager(
... F2FSynthesis(
... {"Evolution": order_2}, filter=lambda node: not is_diagonal(node.op.operator)
... )
... ).run(mixed_circuit())
... )
>>> everywhere[0] - restricted[0] # the gates the filter saved
8
>>> restricted[0] < everywhere[0] and restricted[1] < everywhere[1]
True

gate ที่ filter ประหยัดได้ก็คือตัวที่อันดับสองใช้ไปกับ evolution แนวทแยงพอดี ซึ่งการเปรียบเทียบแนวทแยงข้างต้นคิดราคาไว้ที่แปด (18 เทียบกับ 10) พวกมันไม่ได้อะไรเลยที่นั่น เพราะ evolution นั้นแม่นยำอยู่แล้วที่อันดับหนึ่ง การตัดทิ้งจึงไม่เสียความแม่นยำ

หมายเหตุ

เปรียบเทียบ circuit อันดับสองทั้งสองกันเอง ไม่ใช่กับตัวที่ไม่ขยาย การปล่อยทั้งสอง gate ไม่ขยายจะส่งแต่ละตัวไปยังขั้นตอนจากเฟอร์มิออนเป็น qubit ทั้งตัว ซึ่งเป็นความผิดพลาดที่หมายเหตุ apply=False ข้างต้นอธิบาย: อาจได้จำนวน entangling gate เท่ากับ circuit ที่ filter ในขณะที่เป็นคนละ circuit และความลึกต่างกัน การอ่านประโยชน์ของ filter จากตัวเลขนั้นจึงไม่ได้วัดอะไรเลย

ข้อควรระวัง

ไม่มีตัวเลือกใดตรวจสอบว่าตัวประกอบเป็นเฮอร์มิเชียน และตัวดำเนินการที่จัดกลุ่มไม่ถูกต้องอาจดู ดีกว่า ที่อันดับคู่เมื่อเทียบกับอันดับหนึ่ง ทั้งที่ยังผิด เพราะการทำให้สมมาตรหักล้างความคลาดเคลื่อนบางส่วน จัดคู่สังยุคไว้ในกลุ่มเดียวกัน ดังที่ fermi_hubbard_chain ข้างต้นทำ

ขั้นตอนถัดไป​