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

보손 회로 다루기

BosonicCircuit는 보손 모드 위에서 직접 계산을 표현합니다. 절단도, 인코딩도, 큐비트 수도 담고 있지 않습니다. 이것들은 나중에 트랜스파일 과정에서 들어오며, 그래서 하나의 회로를 다시 만들지 않고도 여러 인코딩과 여러 절단으로 낮출 수 있습니다.

Qiskit의 QuantumCircuit에 비해 의도적으로 축소된 API를 제공하므로, 잘 정의되지 않은 연산이 될 큐비트 게이트를 보손 모드에 적용할 수 없습니다. 이 가이드는 작은 회로들로 더 큰 회로를 조립하기 위해 제공되는 연산들을 다룹니다.

보손 회로를 사용하는 이유​

보손 해밀토니안의 시간 발전을 큐비트 기반 아키텍처에 올리는 기존 워크플로를 생각해 보세요.

  1. 보손 해밀토니안을 정의합니다. 예를 들어 H=−∑ijbi†bjH = -\sum_{ij} b^\dagger_i b_j입니다.

  2. 절단과 인코딩을 선택하고, 이를 사용하여 해밀토니안을 큐비트 연산자로 매핑합니다.

  3. 트로터화나 다른 방법으로 큐비트 공간에서 시간 발전을 구현합니다.

두 번째 단계는 일찍 오며, 되돌리기 비싼 두 가지 결정을 내립니다. 절단은 각 모드가 몇 개의 준위를 유지할지를 정하고, 인코딩은 그것이 큐비트 몇 개의 비용인지를 정하므로, 회로에 대해 다른 작업을 하기 전에 두 가지 모두 큐비트 연산자에 고정됩니다. 같은 물리를 다른 컷오프나 다른 인코딩으로 보려면 해밀토니안을 다시 매핑하고 회로를 다시 만들어야 합니다. 또한 매핑이 버리는 것은 보손 구조입니다. 즉 어떤 항들이 함께 속하는지, 그리고 호핑 쌍의 항들이 서로의 수반이라는 사실입니다.

BosonicCircuit는 그 결정들을 끝으로 옮깁니다.

  1. 보손 해밀토니안을 정의합니다.

  2. 보손 게이트를 사용하여 보손 모드 위에서 직접 시간 발전을 표현합니다.

  3. 트랜스파일의 일부로 절단과 인코딩을 선택합니다.

회로는 무엇을 계산할지를 기술하고, 트랜스파일은 그것을 큐비트에 어떻게 올릴지를 결정합니다. 그러면 같은 회로가 모든 컷오프와 모든 인코딩에서 낮춰지며, 연산자의 그룹화는 이를 활용할 수 있는 패스를 위해 여전히 남아 있습니다. 다음 절들은 이런 회로를 조립하는 방법을 보여주며, 보손 회로 트랜스파일은 그것이 낮춰지는 과정을 보여줍니다.

일반적인 모드 인덱싱​

operators 모듈과 회로는 모드에 대해 하나의 규칙을 공유합니다. 모드는 보손 자유도를 가리키는 인덱스일 뿐이며 그 자체로는 아무 의미도 없습니다. 따라서 OperatorTrait 프로토콜을 구현하는 어떤 연산자든, 무엇을 나타내든 발전시킬 수 있으며, 회로는 모드 2가 무엇을 뜻하는지 알 필요가 없습니다. 모드의 크기도 마찬가지입니다. 회로에는 점유 컷오프가 없으므로, 여기서는 모드가 몇 개의 준위를 갖는지 아무것도 말하지 않습니다.

게이트 추가하기​

게이트는 모드 위에 추가됩니다. BosonicGate 인스턴스만 허용됩니다.

>>> from qiskit_bosons.circuit import BosonicCircuit
>>> from qiskit_bosons.circuit.library import Evolution, InitializeModes
>>> from qiskit_bosons.operators import BosonOperator, ann, cre
>>>
>>> hopping = BosonOperator.from_dict({(cre(0), ann(1)): -1.0, (cre(1), ann(0)): -1.0})
>>> hopping.groups = [0] * len(hopping) # the pair is Hermitian, its terms alone are not
>>>
>>> prepare = BosonicCircuit(2)
>>> prepare.append(InitializeModes([2, 0]), prepare.modes)
>>> step = BosonicCircuit(2)
>>> step.append(Evolution(2, hopping, time=0.1), step.modes)

그 밖의 것은 거부되며, 오류는 문제가 된 유형의 이름을 알려줍니다.

>>> from qiskit.circuit.library import HGate
>>> try:
... prepare.append(HGate(), [0])
... except ValueError as error:
... print(error)
Unsupported instruction type: <class '...HGate'>

회로 합성하기​

compose()는 한 회로의 명령들을 다른 회로 위에 놓습니다. 명령들은 하나의 게이트로 감싸지지 않고 인라인되므로, 결과는 계속 분해하고 트랜스파일할 수 있는 일반 회로입니다.

>>> program = prepare.compose(step)
>>> program.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A composed BosonicCircuit.

기본적으로 새 회로가 반환되고 두 피연산자는 그대로 유지됩니다. 대신 첫 번째 회로를 수정하려면 inplace=True를, 피연산자를 기존 명령들 앞에 놓으려면 front=True를 전달하세요.

>>> step.compose(prepare, front=True)._inner.data == program._inner.data
True

더 좁은 회로는 선택한 모드의 부분집합 위에 놓을 수 있습니다.

>>> single = BosonicCircuit(1)
>>> single.append(Evolution(1, BosonOperator.from_dict({(cre(0), ann(0)): 1.0}), 0.1), single.modes)
>>> wide = BosonicCircuit(3).compose(single, [2])
>>> wide.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A narrow-composed BosonicCircuit.

피연산자로는 다른 BosonicCircuit만 허용됩니다. Qiskit 자체의 compose는 큐비트 게이트를 인라인하여 append가 강제하는 검사를 우회하기 때문입니다.

회로 반복하기​

repeat()는 회로를 처음부터 끝까지 반복합니다. 반복은 평탄화됩니다. 결과는 회로를 감싼 하나의 게이트가 아니라 명령들을 reps번 순서대로 담고 있습니다. 따라서 연산 수는 reps에 비례하여 늘어나며, decompose는 반복 하나를 제거하는 대신 반복된 본문에 작용합니다.

>>> len(step.repeat(0)._inner.data) # zero repetitions is the identity
0
>>> step.repeat(5).draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A repeated BosonicCircuit.

이것이 회로 수준에서 트로터화하는 방법입니다. 짧은 시간 동안 발전시키고 반복합니다. 게이트 자체에서 곱 공식을 선택하는 대안과, 둘이 “반복”이라는 단어만 공유할 뿐 서로 다른 것인 이유는 보손 공간에서 Evolution 게이트 합성을 참조하세요.

배리어로 단계 구분하기​

배리어는 최적화 패스가 그 너머로 게이트를 융합하지 않는 동기화 지점을 표시합니다. barrier()는 모든 모드에, 또는 지정한 모드에 배리어를 하나 놓습니다.

>>> program = BosonicCircuit(2)
>>> program.append(InitializeModes([2, 0]), program.modes)
>>> program.barrier()
>>> program.barrier(program.modes[0], label="zero")
>>> program.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A BosonicCircuit with barriers.

repeat(..., insert_barriers=True)는 연속된 반복 사이마다 하나를 넣고, 마지막 뒤에는 넣지 않습니다.

>>> step.repeat(3, insert_barriers=True).draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A repeated BosonicCircuit with barriers.

배리어와 인코딩​

인코딩은 모드당 여러 큐비트가 필요하므로 모드 레지스터와 큐비트 레지스터의 크기가 다르며, 레이아웃은 레지스터 전체를 서로 연결합니다. 따라서 모드에는 배리어를 좁혀 적용할 자신만의 큐비트가 없습니다. 보손-큐비트 합성 중에 배리어는 그것이 닿았던 각 레지스터의 모든 큐비트에 매핑되며, 이는 어떤 레이아웃에서도 올바릅니다.

>>> from qiskit_bosons.mappers import LevelSpec
>>> from qiskit_bosons.transpiler.presets import generate_preset_occupation_pass_manager
>>> lowered = generate_preset_occupation_pass_manager(LevelSpec(2, "unary_projected")).run(program)
>>> lowered.draw("mpl", fold=-1)
<Figure size ... with 1 Axes>

A QuantumCircuit showing the expansion of barrier to cover the entire qubit register.

보손 회로 읽어내기​

BosonicCircuit에는 measure나 measure_all이 없습니다. 측정된 비트가 무엇을 의미하는지는 인코딩에 따라 달라집니다. 유니터리 인코딩에서는 모드의 점유가 여러 큐비트에 퍼져 있어 비트 패턴으로부터 읽어야 하고, 컴팩트 인코딩에서는 이진수입니다. 따라서 모드를 지정하는 측정은 인코딩을 알아야 하는데, 보손 회로는 의도적으로 그렇지 않습니다. 대신 트랜스파일된 QuantumCircuit을 측정하고, LevelEncoding.level_to_bits()와 낮출 때 사용한 LevelSpec의 오프셋으로 비트열을 디코딩하세요. 인코딩을 선택하여 보스-허バード 동역학 시뮬레이션은 완전한 모델에 대해 이를 수행합니다.

>>> hasattr(BosonicCircuit, "measure_all")
False

다음 단계​