SqDRIFT 회로 생성
샘플 기반 양자 대각화(SQD)에서는 비트열을 샘플링할 앤사츠를 선택해야 합니다. SqDRIFT 변형은 대신 대상 해밀토니안에서 직접 구성한 시간 발전 회로들의 앙상블을 사용합니다. 이는 해밀토니안의 계수에 따라 더 작은 시간 발전 연산자를 부분 샘플링하는 방식으로 이루어지며, 이를 qDRIFT 트로터화 방법이라고 합니다.
이 시작 가이드는 이러한 무작위화된 회로의 앙상블을 생성하는 방법을 보여 줍니다.
1. 해밀토니안 설정
이 가이드에서는 FCIDUMP 파일에서 N2의 전자 구조 해밀토니안을 불러옵니다. FermionOperator를 구성하는 다른 방법도 있으니 해당 문서와 qiskit_fermions.operators.library도 꼭 확인해 보세요.
- PYTHON
- C
>>> from qiskit_fermions.operators.library import FCIDump
>>> from qiskit_fermions.operators import FermionOperator
>>>
>>> fcidump = FCIDump.from_file("docs/guides/n2.fcidump")
>>> num_modes = 2 * fcidump.norb
>>> hamil = FermionOperator.from_fcidump(fcidump)
#include <qiskit_fermions.h>
QfFCIDump* fcidump = NULL;
qf_fcidump_from_file("docs/guides/n2.fcidump", &fcidump);
QfFermionOperator* hamil = qf_ferm_op_from_fcidump(fcidump);
uint32_t num_modes = 2 * qf_fcidump_norb(fcidump);
2. 해밀토니안 항 그룹화
전자 구조 해밀토니안에 존재하는 많은 대칭성을 활용해, 계수가 동일한 관련 항들을 그룹으로 묶습니다. 이 작업은 qDRIFT 프로토콜이 샘플링하는 연산자 계수 분포를 바꾸지만 수렴 보장에는 영향을 주지 않습니다. 특히 대칭성으로 연관된 항들을 그룹화하면 파울리 항이 유리하게 상쇄되어, 그 작용으로 상태를 시간 발전시킬 때 전체 회로 깊이가 더 짧아집니다.
qiskit_fermions.operators.terms.grouping 모듈은 연산자의 항을 그룹화하는 편의 함수를 제공합니다. 자세한 설명은 이 가이드를 참고하세요.
group_terms_by_electronic_structure()의 구현은 해밀토니안의 항이 정규 순서(normal-ordered)로 되어 있다고 가정합니다!
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.grouping import group_terms_by_electronic_structure
>>> from qiskit_fermions.operators.terms.ordering import canonical_order
>>>
>>> canon = canonical_order(hamil.normal_ordered().simplify(atol=1e-16))
>>> exit_code = group_terms_by_electronic_structure(canon, num_modes, two_body_physicist_order=False)
>>> assert exit_code is None
>>> print(canon.groups) # the groups attribute now contains some list of group indices
[0, ...]
QfFermionOperator* normal = qf_ferm_op_normal_ordered(hamil, NULL);
QfFermionOperator* simplified = qf_ferm_op_simplify(normal, 1e-16);
QfFermionOperator* canon = qf_ferm_op_canonical_order(simplified);
QfExitCode exit = qf_ferm_op_group_terms_by_electronic_structure(canon, num_modes, false);
assert(exit == QfExitCode_Success);
qf_ferm_op_free(normal);
qf_ferm_op_free(simplified);
전체 전자 구조 해밀토니안에는 시간 발전 회로에 포함해도 관측되는 비트열에 영향을 주지 않으면서 샘플링 오버헤드만 늘리는 항이 있습니다. 따라서 다음 단계에서 Evolution 게이트를 구성하기 전에, 이 시점에서 해밀토니안에서 그러한 항을 걸러 내는 것이 좋습니다.
이 설명에 해당하는 항은 점유수 기저에서 대각인 항, 즉 수 연산자의 곱()입니다. 여기에는 시간 발전이 회로에 전역 위상만 더하는 상수 에너지 오프셋, 시간 발전이 단일 큐비트 Z 회전에 해당하는 개별 수 연산자, 그리고 같은 고차 곱이 포함됩니다. 이들 중 어느 것도 모드의 점유를 바꿀 수 없으므로, 제거해도 들뜸 내용은 손실되지 않습니다. 5단계의 추출별 거부와 달리 이 필터링은 해밀토니안 자체에 대해 한 번만 수행되므로, 프로토콜이 샘플링하는 계수 분포는 추출 도중에 재정규화되는 것이 아니라 필터링된 연산자로부터 일관되게 다시 만들어집니다.
filter_diagonal_terms() 함수는 이러한 항을 연산자에서 제자리(in place)로 제거합니다.
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.filtering import filter_diagonal_terms
>>>
>>> filter_diagonal_terms(canon)
qf_ferm_op_filter_diagonal_terms(canon);
여기서 한 번만 필터링하는 것이 반복해서 필터링하는 것보다 훨씬 저렴합니다. FermionicQDrift는 트랜스파일된 회로마다 한 번 실행되므로, 호출할 때마다 필터링하지 않고 처음에 해밀토니안을 필터링하면 그로부터 생성되는 모든 회로에서 같은 작업을 반복하지 않아도 됩니다.
같은 이유로 여기서 해밀토니안의 항을 그룹 인덱스 순으로 정렬해 두면 이득입니다. 그룹 인덱스는 항별 태그일 뿐 항이 어디에 있는지는 알려 주지 않으므로, 한 그룹의 항들은 일반적으로 연산자 전체에 흩어져 있습니다. 위에서 적용한 그룹화는 처음 만나는 순서대로 인덱스를 부여하므로, 한 그룹의 두 번째 항이 첫 번째 항에서 멀리 떨어지게 됩니다.
FermionicQDrift는 그룹 전체를 샘플링하고, 추출된 각 그룹을 split_out_groups()로 조회합니다. 흩어진 항들 사이에서 그룹을 찾으려면 모든 항을 훑어야 하므로, 이 조회는 그룹을 하나 뽑든 마흔 개를 뽑든 비용이 같습니다. group_order()는 각 그룹을 하나의 연속 구간으로 모아 주며, 그 후에는 조회가 그룹 경계에 대한 이진 탐색이 되어 보유한 항의 수가 아니라 추출된 그룹의 수만큼만 비용이 듭니다. 조회 자체는 극적으로 저렴해지지만, 스캔이 사라지면 매핑과 합성이 지배적이 되므로 실제 트랜스파일에서 얼마나 절약되는지는 파이프라인의 나머지 작업에 따라 달라집니다.
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.ordering import group_order
>>>
>>> canon = group_order(canon)
>>> print(canon.groups[:4])
[0, 0, ...]
QfFermionOperator* grouped = qf_ferm_op_group_order(canon);
이 작업은 연산자의 값을 바꾸지 않고 항의 순서만 바꾸며, 몇 개의 회로 무작위화를 뽑든 한 번만 하면 됩니다.
3. 시간 발전 회로 준비
해밀토니안의 시간 발전 회로와, 샘플을 뽑을 기본 회로를 준비합니다. qiskit_fermions.circuit.library에는 Qiskit 규칙에 맞춰 이를 수행하는 데 필요한 모든 구성 요소가 있습니다.
qDRIFT 프로토콜은 합성 방법 FermionicQDrift이며, Evolution 게이트는 이를 synthesis로 가지고 있습니다. 이 방법에는 생성할 회로를 사용자 지정할 수 있는 몇 가지 매개변수가 있습니다.
-
각 회로의 길이(들뜸 그룹 수 기준):
num_groups -
선택 사항으로, 난수 생성기의 시드:
rng -
선택 사항으로, 트랜스파일 자체가 병목이 되는 경우를 위한 미리 계산된 샘플링 가중치:
weights(아래의 (선택 사항) 샘플링 가중치 미리 계산하기 참고)
이 방법은 게이트가 보유하므로, 그 가중치처럼 게이트의 연산자를 설명하는 정보는 해당 연산자 바로 옆에 놓입니다.
- PYTHON
- C
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.circuit.library.synthesis import FermionicQDrift
>>>
>>> time = 1.0 # you can choose a desired scaling factor here
>>> num_groups = 10
>>> qdrift = FermionicQDrift(num_groups, rng=19)
>>> evo_gate = Evolution(num_modes, canon, time, synthesis=qdrift)
>>>
>>> circ = FermionicCircuit(num_modes)
>>> circ.append(evo_gate, circ.modes)
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions.
이 예제는 페르미온 모드를 입자로 초기화하지도, 최종 상태를 측정하지도 않습니다.
4. 회로 트랜스파일
qiskit_fermions.transpiler 모듈은 Qiskit의 트랜스파일 파이프라인과 직접 통합되므로, 위에서 구성한 FermionicCircuit을 QuantumCircuit으로 바로 트랜스파일할 수 있습니다.
jordan_wigner() 페르미온-큐비트 매핑을 사용해 페르미온으로 표현된 해밀토니안을 파울리 문자열로 변환합니다. 이는 F2QSynthesis 트랜스파일 패스를 사용해 트랜스파일 과정의 일부로 직접 수행할 수 있습니다. generate_preset_jw_pass_manager()를 사용해 FermionicStagedPassManager를 만들면, 모든 회로 명령어에 Jordan-Wigner 인코딩이 일관되게 사용됩니다.
특히 Qiskit의 Decompose 패스를 트랜스파일 파이프라인의 optimization 단계에 추가하세요. 이 패스는 Evolution 게이트를, 게이트가 가진 합성 방법, 즉 위에서 준비한 qDRIFT 방법으로 분해합니다. 이렇게 하면 회로가 해밀토니안 전체의 시간 발전을 사용하지 않게 되며, 전체 시간 발전은 깊이가 현재 사용 가능한 양자 컴퓨팅 하드웨어의 능력을 넘어섭니다.
대신 회로를 트랜스파일할 때마다 회로별로 고정된 수의 해밀토니안 항 groups를 부분 샘플링합니다. 이를 통해 트랜스파일 파이프라인을 반복 실행하여 SqDRIFT 알고리즘이 요구하는 여러 회로 무작위화를 생성할 수 있습니다. 생성할 회로의 수는 num_sqdrift_randomizations입니다.
- PYTHON
- C
>>> from qiskit.transpiler.passes import Decompose
>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> pm.optimization = FermionicPassManager([Decompose(["Evolution"])])
>>>
>>> num_sqdrift_randomizations = 10
>>> sqdrift_circuits = [
... pm.run(circ) for _ in range(num_sqdrift_randomizations)
... ]
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
앞의 예제들은 FermionicQDrift 합성 방법의 rng 시드를 고정하므로 무작위화 순서를 재현할 수 있습니다.
같은 파이프라인은 다른 두 곳에서도 qDRIFT 방법을 받습니다. F2FSynthesis 패스는 모든 Evolution 게이트에 대한 하나의 방법을 이름으로 받으며(F2FSynthesis({"Evolution": FermionicQDrift(num_groups)})), 어떤 게이트도 자신의 연산자에 특화된 정보를 갖고 있지 않을 때는 이것으로 충분합니다. 다음 단계에서는 세 번째 방법인 QDriftTrotterization 패스를 사용합니다.
5. 점유도에 따라 추출된 들뜸 필터링
이전 단계에서 걸러 낸 대각 항 외에도, 추출된 들뜸이 점유가 이미 고정된(모두 점유되었거나 모두 비점유인) 모드 집합 안에서만 작용하는 경우가 있으며, 이런 들뜸은 한쪽에서 다른 쪽으로 입자를 옮길 수 없습니다. 이러한 들뜸은 샘플링된 비트열에 대해 아무 정보도 주지 않으므로, 여기에 쓴 슬롯은 낭비된 슬롯입니다. 같은 FermionicQDrift 방법을 감싸는 QDriftTrotterization 패스는 이러한 들뜸을 추출되는 즉시 거부하고 대체본을 다시 뽑으므로, 결과 회로의 num_groups개 슬롯 각각이 양쪽을 연결하는 들뜸을 담게 됩니다.
이 필터링은 공짜가 아니며, 그래서 별도의 패스로 제공됩니다. 이 필터링이 보존하는 것은 회로 깊이가 아니라 샘플링된 num_groups개 슬롯이라는 예산입니다. 예산이 고정되어 있으므로 거부된 들뜸은 버려지지 않고 대체되며, 저렴한 들뜸이 합성 비용이 더 큰 결합 들뜸으로 바뀝니다. 같은 시드로 추출한 경우 필터링된 회로가 필터링하지 않은 회로보다 더 깊어질 것으로 예상하세요.
수락된 들뜸에 대해 추출을 재정규화하면 qDRIFT 프로토콜이 샘플링하는 분포가 바뀌며, 2단계의 그룹화와 달리 프로토콜의 수렴 보장도 깨집니다. 샘플링된 곱은 더 이상 전달한 해밀토니안에 따른 발전의 평균과 같지 않습니다. 유지된 모든 들뜸은 수락 확률의 역수만큼 가중되고, 거부된 들뜸은 아무 기여도 하지 않으며, 총 계수 크기나 게이트별 발전 시간은 이를 보상하도록 조정되지 않습니다.
수락 규칙은 또한 과대 근사입니다. 이 규칙은 모드의 지지집합(support)만 비교하므로 진폭 정보를 담지 않으며, 추적되는 집합은 커지기만 합니다. 점유 집합에 갇힌 들뜸은 곱 상태에서는 실제로 비활성이지만, 앞서 수락된 들뜸들이 상태를 얽히게 하고 대부분의 모드를 “불확실”로 표시한 이후의 추출에서는 같은 논리가 약해집니다.
이 필터링은 이 가이드가 설명하는 비트열 샘플링 워크플로, 즉 회로가 SQD 후처리에 입력되고 샘플링된 비트열만 중요한 경우에 사용하세요. 이 회로들로 기댓값을 추정하려는 경우나 다른 방식으로 qDRIFT 오차 한계에 의존하려는 경우에는 사용하지 마세요. 그런 결과에는 이 패스가 보정하지 않는 편향이 포함됩니다.
이 필터링은 어떤 모드가 처음에 점유되어 있는지 알아야 합니다. 따라서 회로에서 Evolution 게이트 앞에 InitializeModes 게이트가 필요하며, InitializeModes.from_hartree_fock()가 이를 구성하는 편리한 방법입니다. 여기서는 N2의 Hartree-Fock 기준 상태(14개 공간 오비탈에 알파 전자 7개, 베타 전자 7개)에 대해 하나를 추가하고, 필터링 유무에 따라 샘플링된 들뜸을 비교합니다.
- PYTHON
- C
>>> from qiskit_fermions.circuit.library import InitializeModes
>>> from qiskit_fermions.transpiler.passes import F2FSynthesis, QDriftTrotterization
>>>
>>> init = InitializeModes.from_hartree_fock(fcidump.norb, (7, 7))
>>>
>>> hf_circ = FermionicCircuit(num_modes)
>>> hf_circ.append(init, hf_circ.modes)
>>> hf_circ.append(Evolution(num_modes, canon, time), hf_circ.modes)
>>>
>>> num_groups = 5
>>> qdrift_unfiltered = F2FSynthesis(
... {"Evolution": FermionicQDrift(num_groups, rng=3480)}
... )
>>> qdrift_trivial = QDriftTrotterization(
... FermionicQDrift(num_groups, rng=3480)
... )
>>>
>>> for instruction in FermionicPassManager(qdrift_unfiltered).run(hf_circ)._inner.data:
... if instruction.operation.name == "Evolution":
... print(sorted(instruction.operation.operator.get_support()))
[2, 4]
[41, 45, 52]
[15, 45, 55]
[41, 52, 53]
[10, 16, 37, 38]
>>>
>>> for instruction in FermionicPassManager(qdrift_trivial).run(hf_circ)._inner.data:
... if instruction.operation.name == "Evolution":
... print(sorted(instruction.operation.operator.get_support()))
[0, 1, 6, 7]
[0, 1, 28, 29]
[4, 13, 55]
[13, 20, 40, 41]
[0, 1, 28, 29]
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
필터링 없이 추출된 들뜸 중 점유 집합(0-6과 28-34)을 건드리는 것은 하나도 없으므로, 어느 것도 점유된 모드와 비점유 모드 사이로 입자를 옮길 수 없습니다. 필터링을 적용하면 다섯 개 모두 거부되고, 점유 모드와 비점유 모드를 실제로 결합하는 들뜸으로 대체됩니다. 예를 들어 처음 수락된 들뜸 [0, 1, 6, 7]은 점유된 모드 0, 1, 6과 비점유 모드 7 사이에서 입자를 옮깁니다.
들뜸이 수락되면 그 지지집합의 모든 모드가 “불확실”해져서 이후 샘플에서 어느 역할이든 맡을 수 있게 되므로, 더 많은 들뜸이 수락될수록 점유 모드 집합과 비점유 모드 집합은 계속 커집니다. 이 때문에 두 번째 들뜸 [0, 1, 28, 29]가 수락될 수 있습니다. 네 모드는 모두 원래 점유되어 있던 것들이므로 원래 비점유였던 어떤 모드와도 결합하지 않습니다. 이 들뜸이 수락되는 것은 첫 번째 들뜸이 모드 0과 1을 건드려 이 모드들이 불확실해졌기 때문입니다(따라서 결합의 “비점유” 쪽으로 간주될 수 있습니다).
이러한 확장 때문에 이 규칙은 검정이 아니라 과대 근사가 됩니다. 더 많은 모드가 불확실해질수록 거부되는 추출이 줄어들며, 필터링이 샘플링 분포에 가장 크게 작용하는 것은 가장 초기의 추출입니다.
필터링이 분포를 얼마나 이동시켰는지 판단하려면, 패스가 회로 메타데이터에 기록하는 거부 횟수를 읽어 보세요. 필터링된 Evolution 게이트마다 하나의 항목이 저장되며, 수락 대 전체 비율은 유지된 들뜸에 과대 가중치가 부여된 비율인 수락 확률을 추정합니다.
- PYTHON
- C
>>> # a freshly seeded pass, so the counts do not depend on earlier draws
>>> qdrift_counted = QDriftTrotterization(
... FermionicQDrift(num_groups, rng=3480)
... )
>>> filtered = FermionicPassManager(qdrift_counted).run(hf_circ)
>>> discarded = filtered.metadata.get("filter_trivial.discarded")
>>> emitted = filtered.metadata.get("filter_trivial.emitted")
>>> print(f"{emitted[0]} kept, {discarded[0]} discarded")
5 kept, 7 discarded
>>> print(f"acceptance probability: {emitted[0] / (emitted[0] + discarded[0]):.2f}")
acceptance probability: 0.42
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
필터링을 하려면 기준이 될 점유 정보가 필요합니다. PrepareSlaterDeterminant 게이트도 InitializeModes와 마찬가지로 이 정보를 제공하며, 모든 OrbitalRotation은 자신이 작용하는 모든 모드를 “불확실”로 표시합니다. 이들이 하나도 없거나, 시드된 모드가 모두 한쪽(모든 모드가 점유되었거나 모든 모드가 비점유)에 놓이면, 패스는 UserWarning을 내고 해당 Evolution 게이트의 샘플링을 필터링하지 않은 채로 둡니다. 남은 항이 더 이상 추적 중인 두 집합을 결합할 수 없는 해밀토니안에서는, 패스가 연속 거부가 QDriftTrotterization.MAX_SAMPLE_RETRIES회에 이른 뒤 RuntimeError를 발생시킵니다.
(선택 사항) 샘플링 가중치 미리 계산하기
트랜스파일 자체가 병목일 때만 이 방법을 사용하세요. 얻는 회로에는 아무런 변화가 없습니다.
FermionicQDrift는 각 그룹을 그 계수의 크기에 비례하는 확률로 추출하며, 이 크기는 호출할 때마다 해밀토니안에서 도출됩니다. 해밀토니안 항당 하나의 값을 구한 다음 그룹당 하나로 줄이는 방식입니다. 이 방법은 상태를 갖지 않으므로 결과가 매번 동일한데도 무작위화마다 이 축약을 반복합니다. weights 인수를 사용하면 group_coeff_means()로 한 번만 계산해 결과를 넘길 수 있으며, 해밀토니안에 의존하는 캐시된 상태를 메서드에 두지 않고도 비용을 루프 밖으로 옮길 수 있습니다.
이는 2단계의 항 정렬과 짝을 이룹니다. 그쪽은 그룹 조회를 루프 밖으로 빼내고 이쪽은 가중치 축약을 빼내는데, 한쪽이 없을 때에야 다른 쪽이 눈에 띄므로 둘을 함께 적용하는 것이 좋습니다. 위에서 이미 canon을 재정렬했으므로 둘은 그대로 결합됩니다.
가중치는 특정 연산자의 그룹을 설명하므로, 그 연산자의 Evolution 게이트와 함께 두어야 합니다. 그 게이트용 방법을 만들어 3단계와 똑같이 게이트의 synthesis로 넘기세요.
- PYTHON
- C
>>> from qiskit_fermions.operators.terms.grouping import group_coeff_means
>>>
>>> weights = group_coeff_means(canon) # `canon` is group-ordered, from step 2
>>> qdrift_weighted = FermionicQDrift(10, rng=19, weights=weights)
>>> weighted_gate = Evolution(num_modes, canon, time, synthesis=qdrift_weighted)
>>>
>>> weighted_circ = FermionicCircuit(num_modes)
>>> weighted_circ.append(weighted_gate, weighted_circ.modes)
>>>
>>> weighted_circuits = [
... pm.run(weighted_circ) for _ in range(num_sqdrift_randomizations)
... ]
>>> len(weighted_circuits)
10
// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
이 가중치는 패스가 직접 계산했을 값과 정확히 같으므로 회로는 이전과 같고, 반복되는 축약만 사라집니다.
연산자가 그룹을 가지고 있으면 그룹 단위로 샘플링되므로, 항마다가 아니라 그룹마다 하나의 항목이 필요합니다. 항목은 음수가 아니어야 합니다. qDRIFT 프로토콜은 를 양수 로 분해하므로 가중치는 크기이며, 계수의 부호는 에 속하고 패스가 연산자에서 직접 읽어 냅니다.
가중치는 상대적 선호도가 아니라 절대적인 크기입니다. 이 가중치의 합은 발전 시간도 결정하므로, 모든 항목에 를 곱하면 샘플링은 동일하지만 대신 만큼 발전합니다. 따라서 해밀토니안 자체의 계수 크기가 아닌 다른 값을 전달하면 앙상블이 근사하는 발전이 달라지며, 이 때문에 이것은 샘플링을 조정하는 방법이 아니라 성능 도구입니다.
가중치는 자신이 설명하는 연산자를 가진 게이트에 속하므로, 회로에는 Evolution 게이트가 여러 개 있을 수 있고 각각 자기 연산자의 가중치를 가집니다. 5단계의 QDriftTrotterization 패스도 같은 방식으로 이를 따릅니다. 게이트가 가진 FermionicQDrift로 게이트를 추출하고, 아무것도 갖고 있지 않은 게이트에는 패스가 생성될 때 받은 것을 대신 사용합니다. 게이트에 있는 다른 종류의 합성 방법은 qDRIFT에 관한 설정이 아니므로 패스가 무시합니다. F2FSynthesis({"Evolution": ...})처럼 게이트 이름으로 패스에 방법을 주면 모든 Evolution 게이트에 하나의 방법이 적용되므로, 여기서 전달한 가중치는 게이트가 하나뿐인 회로에서만 의미가 있습니다. 가중치의 길이는 적용되는 각 게이트에 대해 검사됩니다.
(선택 사항) 페르미온 모드 인덱싱 최적화
페르미온 모드 인덱스를 다시 매겨 페르미온 들뜸 범위의 거리를 최소화하는 최적화 단계를 트랜스파일 파이프라인에 추가할 수 있습니다. 이 최적화는 SqDRIFT 논문에서 도입되었으며 build_excitation_span_minimization_model()로 구현되어 있습니다. RelabelModes 패스를 사용하면 트랜스파일러 파이프라인에 쉽게 삽입할 수 있습니다.
- PYTHON
- C
>>> from pyomo.environ import SolverFactory
>>> from qiskit_fermions.transpiler.passes import RelabelModes
>>>
>>> solver = SolverFactory("appsi_highs")
>>> solver.options["time_limit"] = 10
>>>
>>> qdrift = F2FSynthesis({"Evolution": FermionicQDrift(5, rng=19)})
>>> relabel = RelabelModes(solver=solver)
>>>
>>> pm.optimization = FermionicPassManager([qdrift, relabel])
>>>
>>> relabeled_circ = pm.run(circ)
>>> # if the automatic mode relabeling was successful, the circuit's
>>> # metadata will contain the mode `permutation` information
// WARNING: This feature is not available via the C API.
RelabelModes 내부의 자동 최적화(build_excitation_span_minimization_model()를 활용)을 사용하려면 HAS_PYOMO가 관리하는 선택적 의존성이 필요합니다.
레이블 재지정은 주어진 큐비트가 담당하는 모드를 바꾸므로, 이를 반영해야 부분공간 대각화가 올바른 비트열을 얻습니다. 어디에서 반영하는지는 측정을 어디에서 하느냐에 따라 달라집니다.
트랜스파일하기 전에 FermionicCircuit.measure_all()로 FermionicCircuit에 측정을 추가하면 따로 할 일이 없습니다. 각 측정이 자신이 읽는 모드를 지정하므로 레이블 재지정이 이를 함께 옮기고, 샘플링된 비트열은 원래의 모드로 인덱싱됩니다.
대신 트랜스파일된 QuantumCircuit을 측정하면, 회로 메타데이터에 기록된 permutation을 사용해 비트열을 후처리해야 합니다. 방법은 RelabelModes 문서를 참고하세요.
다음 단계
이제 회로 앙상블을 성공적으로 생성했으므로 이 회로들에서 비트열을 샘플링할 수 있습니다. 이를 위해서는 회로를 하드웨어에서 실행해야 합니다. 자세한 안내는 Qiskit 문서를 참고하세요.
비트열 샘플을 얻고 나면, SQD 애드온 튜토리얼에서 설명하듯이 해밀토니안 계수와 함께 사용해 SQD 후처리를 수행할 수 있습니다.