주 콘텐츠로 건너뛰기
이 프로젝트는 베타 단계입니다. 버그를 발견하셨나요? GitHub 이슈 만들기. 질문이나 아이디어가 있으신가요? Discussions에서 질문하기 — 피드백을 기다립니다!
이 페이지의 번역 품질은 어떻습니까?

연산자 항 묶기: 연산자 구조 활용

BosonOperator는 각 항에 그룹 인덱스를 태그할 수 있습니다. 태그 자체에는 의미가 없으며, 어떤 항들이 함께 속하는지만 알려줍니다. 태그가 무엇을 뜻하는지는 그것을 사용하는 쪽이 결정합니다. 이 패키지에서 사용하는 쪽은 그룹마다 하나의 인자로 분할되는 시간 발전 게이트와, 그룹 전체를 샘플링하는 무작위 qDRIFT 패스입니다.

이 가이드는 그룹을 할당하는 방법, 사용하는 쪽이 의존하는 속성을 확인하는 방법, 그리고 큰 연산자에서 몇 개의 그룹을 골라내는 일이 저렴하게 유지되도록 연산자를 배치하는 방법을 보여줍니다.

참고

이 패키지에는 자동 그룹화 전략이 포함되어 있지 않습니다. 모델에 대해 알고 있는 바에 따라 인덱스를 직접 할당합니다.

그룹 할당하기​

실행 예제는 3사이트 보스-허バード 체인입니다. 각 결합은 호핑 쌍 하나를 기여하고, 온사이트 상호작용은 대각 조각을 이룹니다. 그룹화된 연산자를 만드는 가장 간단한 방법은 from_terms_with_groups()이며, 각 항을 그룹 인덱스와 함께 (actions, coefficient, group) 세 쌍으로 받습니다. 항을 적어 내려가는 순간, 그 항이 왜 거기에 속하는지 아직 알고 있을 때 그룹을 결정합니다.

>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>>
>>> bonds = [(0, 1), (1, 2)]
>>> terms = []
>>> for group, (left, right) in enumerate(bonds): # one group per bond
... terms.append(([cre(left), ann(right)], -1.0, group))
... terms.append(([cre(right), ann(left)], -1.0, group))
>>> for site in range(3): # the on-site interactions share one group
... terms.append(([cre(site), cre(site), ann(site), ann(site)], 1.0, len(bonds)))
>>> hamiltonian = BosonOperator.from_terms_with_groups(terms)
>>> len(hamiltonian), hamiltonian.num_groups()
(7, 3)

항은 주어진 순서대로 저장되므로, 그룹 인덱스가 잘못된 항에 붙는 일은 생기지 않습니다.

이미 존재하는 연산자는 사후에 groups 속성을 할당하여 그룹화해야 합니다. from_dict()는 항을 저장하는 순서를 지정하지 않으므로 이 부분에서 주의가 필요합니다. 위치별로 목록을 적어 내리면 인덱스가 잘못된 항에 붙을 수 있습니다. 대신 항 자체에서 각 인덱스를 도출하세요.

>>> existing = BosonOperator.from_dict({tuple(actions): coeff for actions, coeff, _ in terms})
>>> existing.groups = [
... len(bonds)
... if len({mode for _, mode in term}) == 1
... else bonds.index(tuple(sorted({mode for _, mode in term})))
... for term, _ in existing.iter_terms()
... ]
>>> existing.num_groups(), existing.equiv(hamiltonian)
(3, True)

인덱스의 수는 항의 수와 일치해야 합니다. 그렇지 않으면 거부되는데, 너무 짧은 목록은 항을 그룹과 함께 순회하는 모든 곳에서 뒤쪽 항을 조용히 누락시키기 때문입니다.

>>> try:
... hamiltonian.groups = [0, 1]
... except ValueError as error:
... print(error)
expected one group index per term, but got 2 for 7 terms

소비자가 의존하는 것 확인하기​

qiskit_bosons.operators.terms의 세 함수는 기존 그룹화에 대해 각각 하나의 좁은 질문에 답합니다. 정확성은 사용하는 쪽에 상대적이므로, 어느 것도 그룹화가 “올바르다”고 말하지는 않습니다.

각 그룹이 에르미트인가요? 이것이 가장 중요한 속성입니다. 그룹의 시간 발전은 그룹이 에르미트일 때만 유니터리이며, 단독 호핑 항 bi†bjb^\dagger_i b_j는 에르미트가 아닙니다. 수반과의 쌍만 에르미트입니다.

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

호핑 쌍을 분할하는 그룹화는 즉시 잡아냅니다.

>>> split = BosonOperator.from_terms_with_groups(
... [(actions, coeff, index) for index, (actions, coeff, _) in enumerate(terms)]
... ) # one term per group
>>> [bool(flag) for flag in groups_are_hermitian(split)]
[False, False, False, False, True, True, True]

네 개의 호핑 항은 검출됩니다. 세 개의 True 항목은 그 자체로 에르미트인 온사이트 항 bi†bi†bibib^\dagger_i b^\dagger_i b_i b_i입니다. 하나의 항 안에서 모드가 반복되는 것은 정당할 수 있으며, 그렇다고 해서 무언가가 버려지지는 않습니다. 플래그는 terms의 순서를 따르므로, 각 플래그를 그것이 설명하는 항과 대조해 읽을 수 있습니다.

계수가 균일한가요? 무작위화된 곱 공식은 그룹을 계수의 평균 크기로 평가하며, 이는 크기가 서로 일치할 때만 그룹을 충실하게 나타냅니다.

>>> from qiskit_bosons.operators.terms import group_coeff_means, groups_have_uniform_coeffs
>>> groups_have_uniform_coeffs(hamiltonian)
[True, True, True]
>>> group_coeff_means(hamiltonian)
[1.0, 1.0, 1.0]

두 검사는 서로를 함의하지 않습니다. 계수가 균일하다고 해서 그룹이 에르미트가 되는 것은 아니며, 에르미트 그룹이라도 크기가 섞여 있을 수 있습니다. 기본적으로는 크기를 비교하는데, group_coeff_means()가 평균을 내는 대상이 바로 그것이기 때문입니다. 복소 계수를 가진 에르미트 쌍은 크기는 같지만 계수는 같지 않으므로, 기본 형태만 만족합니다.

>>> conjugate = BosonOperator.from_terms_with_groups(
... [([cre(0), ann(1)], 1.0j, 0), ([cre(1), ann(0)], -1.0j, 0)]
... )
>>> groups_have_uniform_coeffs(conjugate)
[True]
>>> groups_have_uniform_coeffs(conjugate, abs=False)
[False]
참고

groups_are_hermitian()은 is_hermitian()를 기반으로 만들어졌으며, 이 함수는 정규 순서화 후 연산자를 그 수반 연산자와 비교합니다. 이는 절단되지 않은 대수의 항등식이므로, 통과한 그룹은 보손 연산자로서 에르미트입니다. 사다리 연산자의 곱이 어떻게 정렬되고 비교되는지는 연산자 표현의 설계 원칙을 참고하세요.

그룹별로 항 정렬하기​

split_out_groups()는 요청한 그룹마다 하나의 연산자를 반환합니다. 한 그룹의 항이 연산자 전체에 흩어져 있으면 이를 찾기 위해 모든 항을 훑어야 합니다. 각 그룹이 하나의 연속 구간을 이루면 대신 그룹 경계를 이진 탐색하므로, 조회 비용은 보유한 항의 수가 아니라 요청한 그룹의 수를 따릅니다.

group_order()가 그런 배치를 만들어 줍니다. 이는 그룹 인덱스에 대한 안정 정렬입니다. 연산자의 값은 바뀌지 않으며, 그룹 내 항들은 상대적인 순서를 유지합니다.

>>> from qiskit_bosons.operators.terms import group_order
>>> scattered = BosonOperator.from_terms_with_groups(
... [(actions, coeff, group) for (actions, coeff, _), group in zip(terms, [1, 0, 1, 0, 2, 2, 2])]
... )
>>> ordered = group_order(scattered)
>>> ordered.groups
[0, 0, 1, 1, 2, 2, 2]
>>> ordered.equiv(scattered)
True

두 배치 모두 같은 연산자를 반환하며, 비용만 다릅니다.

>>> request = [2, 0]
>>> all(
... a.equiv(b)
... for a, b in zip(
... scattered.split_out_groups(request), ordered.split_out_groups(request), strict=True
... )
... )
True

다음 단계​

그룹화는 모델의 구조가 아직 보이는 여기, 보손 수준에서 선택됩니다.