AWS 기술 블로그
Amazon Braket으로 양자-고전 하이브리드 알고리즘 실행하기 (2편)
본 블로그는 Amazon Braket 하이브리드 알고리즘 시리즈의 두 번째 글입니다.
각 편의 구성은 다음과 같습니다.
- 1편: 알고리즘 스크립트 방식의 하이브리드 잡 환경 구성
- 2편 (현재 글): @hybrid_job 데코레이터 방식과 로컬 잡 활용
지난 블로그에서는 알고리즘 스크립트 방식에 기반한 하이브리드 잡 환경을 구성하는 방법에 대해 소개하였습니다. 이번 블로그에서는 @hybrid_job 데코레이터를 이용한 하이브리드 잡 환경을 구성하는 방법에 대해 알아보도록 하겠습니다.
해결하고자 하는 문제에 대한 정의 및 수학적 모델링
이번에 해결하고자 하는 문제는, 지난 블로그에서 다뤘던 문제보다는 다소 복잡합니다. ‘0’으로 초기화된 큐비트에 “X 축 회전 게이트(RX)”를 적용했을 때, 각도의 변화에 따라 관측 가능한 ‘Z’의 기댓값은 어떻게 변화하는지를 확인하는 문제입니다. ‘Z’ 연산자의 기댓값은 큐비트 상태를 측정 및 분석하는 기본 측정량 중 하나입니다. 여러 축(X, Y, Z)의 기댓값을 함께 측정해 단일 큐비트의 상태를 완전히 재구성하는 과정을 양자 상태 토모그래피(state tomography)라고 하며, 이 예제에서는 그중 Z 축 측정만 다룹니다. 이 기댓값이 0이면 큐비트를 측정했을 때 |0⟩과 |1⟩이 50:50 확률로 관측된다는 의미이고, +1에 가까우면 상태가 |0⟩에, -1에 가까우면 상태가 |1⟩에 가깝다는 뜻입니다.
이 내용을 좀 더 명확히 설명하기 위해, 수학 연산으로 치환해 보도록 하겠습니다. “X 축 회전 게이트(RX)”와 ‘0’ 상태는 다음과 같이 행렬 형태로 표현됩니다.

‘0’ 상태(|0⟩ )에 회전 연산자 Rx(θ)를 적용하여 측정된 새로운 큐비트 상태 |φ⟩는 아래와 같습니다.


여러분들도 잘 아시는 파울리(Pauli) ‘Z’ 게이트는 수학적으로 다음과 같이 표현됩니다.

이때, 현재 양자 상태인 |φ⟩에 대한 ‘Z’ 기댓값의 중간 계산 과정은 생략했습니다만, 다음과 같은 결과를 도출할 수 있습니다.

즉, ‘Z’ 연산자의 기댓값은 cos θ이므로, 회전에 따라 기댓값이 변하게 되는 것을 알 수 있습니다. 따라서 이 값은 -1에서 +1 사이의 값을 가질 것으로 쉽게 예측할 수 있습니다.
고전 컴퓨팅과 양자 컴퓨팅 자원을 함께 사용하는 하이브리드 알고리즘 정의
다음에 소개하는 코드는 Amazon Braket 환경에서 동작하는 하이브리드 양자-고전 알고리즘의 한 예시입니다. 함수 이름은 run_algorithm이며, 네 개의 입력값을 받습니다.: 1) 사용할 양자 디바이스(device), 2) 반복 횟수(n_iterations), 3) 각도 변화의 크기(step_size), 4) 그리고 회로를 실행할 때의 측정 횟수(n_shots)입니다. 이 함수에서 Z 기댓값을 수학적으로 직접 계산하는 것이 아니라, 실제로 양자 회로를 여러 번 실행(측정)하여 얻은 결과값을 바탕으로 기댓값을 확인하게 됩니다.
from braket.circuits import Circuit, FreeParameter, Observable
from braket.jobs.metrics import log_metric
import math
def run_algorithm(device, n_iterations, step_size, n_shots):
example_circuit = Circuit().rx(0, FreeParameter("theta")) # 양자 회로 정의
example_circuit.expectation(observable=Observable.Z(), target=0)
print("Quantum circuit:")
print(example_circuit)
results = {}
for idx in range(n_iterations):
print(f">>> Iteration {idx}")
rx_angle = math.pi * idx * step_size # => 3.14*idx(0,1,2,..,5)*step_size(0.1)
result = device.run(
example_circuit,
shots=n_shots,
inputs={"theta": rx_angle} # iteration 만큼 각도가 바뀐다
).result()
exp_val = result.values[0]
log_metric(metric_name="rotation_angle", value=rx_angle, iteration_number=idx)
log_metric(metric_name="expectation_value", value=exp_val, iteration_number=idx)
results[idx] = { # 로컬 메모리에 결과 저장
'rx_angle': rx_angle,
'expectation_value': exp_val
}
return results
먼저, 함수는 하나의 큐비트에 파라미터화된 RX 회전 게이트를 적용하는 간단한 양자 회로 ‘example_circuit‘를 정의합니다. 이때 각도는 ‘theta‘라는 이름의 자유 파라미터로 남겨둡니다. 그리고 회로의 0번 큐비트에 대해 Z 관측량(Observable)의 기댓값을 측정하도록 설정합니다. 이 회로의 구조는 ‘print(example_circuit)‘에 의해 그림1과 같이 주피터 노트북에 출력됩니다.

<그림1. 단일 큐비트 파라미터 회로: Rx(θ) 회전과 Z 기댓값 측정>
이제 알고리즘의 핵심 반복문이 시작됩니다. 반복문은 입력값인 n_iterations만큼 반복되며, 각 반복마다 현재 반복 인덱스(idx)에 따라 RX 게이트의 회전 각도를 계산합니다. 각도는 π(파이)에 현재 반복 인덱스와 step_size를 곱해서 정해집니다. 예를 들어, step_size가 0.1이고 idx가 2라면, 각도는 π × 2 × 0.1이 됩니다.
계산된 각도는 양자 회로의 파라미터 ‘theta‘에 입력값으로 전달됩니다. 그런 다음, 지정된 양자 디바이스에서 회로를 n_shots만큼 실행하고, 결과를 받아옵니다. 이 결과에서 Z 관측량의 기댓값을 추출합니다.
각 반복마다 1) 사용된 회전 각도와 2) 얻어진 기댓값을 log_metric 함수로 기록하면, 실행하는 동안 각 반복의 값이 그림2처럼 주피터 노트북에 로그로 출력됩니다. 각도를 바꿔가며 기댓값이 어떻게 변하는지 실시간으로 확인할 수 있습니다.

<그림2. 주피터 노트북에서 확인 가능한 알고리즘 실행 로그 — 반복별 회전각과 기댓값 기록>
또한, 각 반복의 결과는 로컬(외부 저장소가 아닌 실행 중인 파이썬 메모리) 딕셔너리(키(key)-값(value) 쌍으로 데이터를 담는 자료구조)에도 저장되어, 함수가 종료될 때 모든 반복의 결과를 한 번에 반환할 수 있도록 합니다.
요약하면, 이 코드는 파라미터화된 양자 회로를 여러 번 실행하면서, 각도에 따른 기댓값 변화를 측정 및 기록하는 하이브리드 알고리즘의 전형적인 구조를 보여줍니다. 고전 컴퓨터는 각도를 계산하고, 양자 디바이스(SV1 시뮬레이터)는 회로를 실행하여 결과를 제공합니다. 실험 결과는 각 반복마다 로그로 출력(기록)되고, 모든 반복이 끝나면 결과가 한 번에 반환됩니다.
양자 알고리즘을 실행하는 방법
아래 코드는 위의 알고리즘을 6번 반복 수행시키며, 1) 양자 디바이스 타입, 2) 스텝 사이즈, 3) 양자 회로 샷의 개수를 run_algorithm 함수에 전달합니다. 그리고 결과값을 ‘results‘에 저장합니다. 이 코드의 실행 결과가 바로 그림2의 결과입니다.
from braket.aws import AwsDevice
from braket.devices import Devices
from pprint import pprint
device = AwsDevice(Devices.Amazon.SV1)
results = run_algorithm(device, n_iterations=6, step_size=0.1, n_shots=200)
pprint(results)
이 코드를 유의 깊게 볼 필요가 있는 이유는, 고전과 양자 리소스를 모두 사용하는 상황에서 Amazon Braket 하이브리드 잡 기능을 사용하지 않고 일반 Amazon Braket 실행 코드를 사용했다는 점입니다. 즉 실행 코드에서 하이브리드 데코레이터(@hybrid_job)나 지난 블로그에서 소개한 AwsQuantumJob을 사용하지 않았습니다. 이런 방식으로 실행하면, 당연하게도 결과는 Amazon Braket 콘솔의 ‘Hybrid jobs’에서는 확인할 수 없고, 그림3과 같이 콘솔의 ‘Quantum tasks’ 항목에서만 확인할 수 있습니다.

<그림3. 하이브리드 잡 기능을 사용하지 않은, 하이브리드 알고리즘의 수행 결과(6회 실행)>
이렇게 하이브리드 잡을 사용하지 않고 양자 및 고전 리소스를 함께 쓰는 방식의 장단점을 알아보겠습니다.
현재 코드의 장점
- 복잡한 환경 설정이나 별도의 클라우드 리소스 관리 없이, 파이썬 코드만으로 양자 실험을 바로 시작할 수 있습니다. 하이브리드 잡 같은 별도 기능을 쓰지 않아도 되기 때문에 입문자도 어렵지 않게 실행할 수 있고, 코드를 작성해 실행하는 것만으로 양자 회로 설계, 파라미터 변화, 결과 측정 같은 기본 원리를 직접 체험할 수 있습니다.
- 고전 컴퓨터와 양자 시뮬레이터의 결합 구조가 코드에 그대로 드러나 있어, 알고리즘이 실제로 어떻게 동작하는지 한눈에 이해할 수 있습니다. 각 단계(회로 생성 → 실행 → 결과 저장)가 순차적으로 표현되어 있어 흐름을 따라가기 쉽고, 실험 조건 변경도 간단합니다.
- 실험 결과가 함수의 반환값으로 바로 제공됩니다. 각 반복의 결과를 로컬 딕셔너리에 모아두었다가 함수가 끝나는 순간 한꺼번에 반환하므로, 콘솔이나 외부 시스템을 거치지 않고 내 코드에서 곧바로 결과를 받아 추가 분석이나 시각화에 활용할 수 있습니다.
- 하이브리드 잡이 추가로 사용하는 별도의 고전 인스턴스 비용 없이 간단한 실험에 집중할 수 있습니다. 복잡한 자동화나 대규모 실험이 필요 없는 상황에서는, 이렇게 직접 코드를 실행하는 방식이 오히려 더 직관적이고 효율적일 수 있습니다.
요약하면, 현재 코드는 환경 설정이 간단하고, 실험 흐름을 쉽게 파악할 수 있으며, 결과를 즉시 확인할 수 있다는 점에서 입문자나 소규모 실습에 매우 적합합니다.
현재 코드의 단점
- 실험의 모든 과정을 사용자가 직접 관리해야 한다는 점이 가장 큰 한계입니다. 반복 실행, 결과 저장, 오류 처리까지 모두 직접 코드로 챙겨야 합니다. 반면 하이브리드 잡을 사용하면 실행 환경 준비, 로그 및 지표 수집, 체크포인트 저장, 재시도 같은 인프라 관리를 관리형 환경이 대신 처리해 주기 때문에, 사용자는 알고리즘 자체에 더 집중할 수 있습니다. (다만 반복문 로직은 어느 방식이든 사용자 코드에 있습니다.)
- 실험이 길어지거나 복잡해지면 안정성 문제가 생길 수 있습니다. 내 컴퓨터가 중간에 꺼지거나 네트워크 문제가 발생하면 진행 중이던 결과가 사라질 수 있고, 중단된 실험을 이어서 실행하기도 어렵습니다. 하이브리드 잡은 관리형 실행 환경 위에서 동작하고 체크포인트 저장과 중단 후 재시작을 지원하므로, 장시간 및 대규모 실험에 더 적합합니다.
- 결과 관리 측면에서도 한계가 있습니다. 이 코드는 결과를 함수 반환값, 즉 실행 중인 프로세스의 메모리(로컬 변수)에만 담아두기 때문에, 그림4와 같이 노트북 세션이 종료되면 사라집니다. 따라서 결과를 오래 보관하거나 다른 사람과 공유하려면 JSON이나 CSV 같은 파일로 별도로 저장해야 합니다. 반면 하이브리드 잡을 사용하면 결과와 로그가 AWS 콘솔 및 Amazon CloudWatch에 자동으로 남아 언제든 웹에서 확인하고 공유할 수 있습니다.
- 실제 양자 하드웨어(QPU)를 사용할 때도 차이가 있습니다. QPU는 여러 사용자가 함께 접근하기 때문에 작업이 대기열에 오래 머물 수 있는데, 하이브리드 잡을 사용하면 해당 QPU에 우선 접근권이 주어져 대기를 줄일 수 있습니다.
- 마지막으로, 실험이 복잡해지고 반복 규모가 커질수록 코드를 계속 손봐야 해 확장성이 떨어집니다. 하이브리드 잡은 여러 실험의 병렬 제출과 대규모 실험 관리에 유리해, 복잡한 알고리즘 실험에 더 효율적입니다.

<그림4. 일반 실행과 하이브리드 잡 실행의 결과 및 로그 저장 방식 비교>
요약하면, 현재 코드는 모든 과정을 사용자가 직접 관리해야 하고, 장시간 및 대규모 실험에서 안정성과 결과 보존이 취약하며, 규모가 커질수록 확장성이 떨어진다는 점에서 본격적인 대규모 실험에는 한계가 있습니다. 간단한 실습이나 짧은 실험에는 적합하지만, 안정성·자동화·결과 관리가 중요해지는 순간부터는 하이브리드 잡을 활용하는 편이 훨씬 편리하고 안전합니다.
| 구분 | 하이브리드 잡 미사용 | 하이브리드 잡 사용 |
|---|---|---|
| 코드 구조/단순성 | 코드가 간단하고 이해하기 쉬움 | 초기 설정과 코드 분리가 필요함 |
| 시작/실행 용이성 | 바로 실행 가능, 빠른 시작 | 잡 제출, 인스턴스 준비 등 추가 단계 필요 |
| 확장성 | 대규모 및 반복 실험의 관리와 확장이 어려움 | 대규모 및 복잡한 하이브리드 알고리즘 구현 용이 |
| 자원 관리 | 로컬 CPU 및 메모리 등 직접 관리 필요 | Amazon Braket이 자동 관리 |
| 실행 시간 | 반복마다 QPU 대기열에서 대기가 누적될 수 있음 | QPU 우선 액세스로 대기 시간 단축 가능 |
| 모니터링 | 콘솔 대시보드, 그래프 없음(*)
(로그는 stdout으로만) |
콘솔에서 실시간 메트릭 모니터링 |
| 결과 저장 | 로컬 환경에 저장, Amazon S3 자동 연동 아님 | Amazon S3 자동 저장 관리 |
| 고전 컴퓨팅 리소스 | 로컬 리소스(CPU, 메모리) 사용 | AWS 클라우드 인스턴스 자동 할당 |
| 결과 형태 | 일반 task로 결과 생성 | 하이브리드 잡 결과로 통합 관리 |
<표1. 하이브리드 잡 미사용(일반 task) vs 사용 비교>
(*) 모니터링 항목의 “그래프 없음”은 Amazon Braket 콘솔이 제공하는 자동 메트릭 대시보드 기준입니다. 노트북에서 matplotlib 등으로 결과를 직접 시각화하는 것은 미사용 시에도 가능합니다.
Amazon Braket 하이브리드 잡을 이용한 하이브리드 알고리즘 수행
이번에는 위 문제를 Amazon Braket에서 제공하는 @hybrid_job 데코레이터 기능을 이용해서 해결해 보도록 하겠습니다. 그림5는 일반적인 Amazon Braket의 하이브리드 아키텍처를 보여주고 있습니다. 사용자는 개발 환경(예를 들어 주피터 노트북 인스턴스 또는 자신의 로컬 컴퓨터)에서 알고리즘 코드를 작성하고, Amazon Braket을 통해 하이브리드 잡을 제출합니다. 잡이 제출되면 Amazon Braket 서비스가 이를 받아들이고, 잡 실행을 위한 별도의 잡 EC2 인스턴스를 생성합니다. 이 잡 인스턴스는 코드를 개발하는 주피터 노트북 인스턴스와는 별개의 리소스입니다. 앞선 방식에서는 이 별도의 인스턴스가 생성되지 않는다고 설명하였습니다.
잡 인스턴스 내부에는 그림5와 같이 잡 컨테이너가 생성되어, 사용자가 작성한 알고리즘 코드가 격리된 환경에서 실행됩니다. 이 컨테이너는 고전 연산(클래식 컴퓨팅)과 양자 연산 요청을 모두 관리합니다. 알고리즘이 양자 회로 실행을 요청할 경우, 잡 인스턴스는 해당 요청을 클라우드 기반 시뮬레이터(예: SV1) 또는 실제 양자 하드웨어(QPU)로 전송합니다.

<그림5. 일반적인 Amazon Braket 하이브리드 잡 아키텍처>
아래 코드는 Amazon Braket의 하이브리드 잡(hybrid job) 기능을 이용해, 양자-고전 하이브리드 알고리즘을 관리형 환경에서 실행하고, 실행 과정의 결과와 비용을 함께 관리하는 예제입니다.
from braket.jobs import hybrid_job
from braket.tracking import Tracker
# 할당된 양자 디바이스 식별
from braket.jobs.environment_variables import get_job_device_arn
@hybrid_job(device=Devices.Amazon.SV1)
def hello_world(n_iterations, step_size, n_shots):
print("Execution started")
cost_tracker = Tracker().start()
device = AwsDevice(get_job_device_arn())
results = run_algorithm(device, n_iterations, step_size, n_shots)
cost_tracker.stop()
print("Execution completed")
return {
"results": results,
"task summary": cost_tracker.quantum_tasks_statistics(),
"estimated cost": float(cost_tracker.simulator_tasks_cost() + cost_tracker.qpu_tasks_cost()),
}
job = hello_world(n_iterations=11, step_size=0.1, n_shots=1000)
print(f"{job.arn} {job.state()}")
hello_world 함수는 @hybrid_job 데코레이터로 감싸져 있습니다. 이 데코레이터는 해당 함수가 Amazon Braket 하이브리드 잡 환경에서 실행되도록 만들어 줍니다. 함수는 1) 알고리즘 반복 횟수, 2) 각도 변화의 크기, 3) 회로 실행(n_shots) 횟수를 입력값으로 받습니다.
함수가 실행되면 먼저 “Execution started”를 출력해 시작을 알립니다. 이어서 비용 추적을 위한 Tracker 객체를 생성해 추적을 시작하고, 현재 잡에 할당된 양자 디바이스의 식별자(ARN)를 얻어와 이를 바탕으로 디바이스 객체를 생성합니다.
다음은 실제 알고리즘 실행 단계로, 블로그 앞단에서 정의한 run_algorithm 함수를 호출해 할당된 디바이스와 입력값으로 알고리즘을 실행하고 결과를 받아옵니다. 실행이 끝나면 비용 추적을 종료하고 “Execution completed”를 출력합니다. 마지막으로 알고리즘 실행 결과, 태스크 요약 통계, 시뮬레이터 및 QPU 태스크의 예상 비용을 하나의 딕셔너리로 묶어 반환합니다.
여기서 한 가지 유의할 점이 있습니다. @hybrid_job으로 감싼 함수는 주피터 노트북이 아니라 AWS가 띄운 별도의 관리형 인스턴스에서 실행됩니다. 따라서 함수 안의 “Execution started”, “Execution completed” 같은 출력은 노트북 화면이 아니라 해당 잡의 로그(CloudWatch)에 기록됩니다. 실행 로그를 보려면 job.logs()를 호출하거나 Braket 콘솔의 Hybrid jobs → 해당 잡 → Hybrid job logs에서 확인하면 됩니다.
hello_world를 호출(job = hello_world(...))하면 Amazon Braket 하이브리드 잡 환경에 작업이 자동으로 제출 및 실행됩니다. 이때 주의할 점은 job에 담기는 값입니다. @hybrid_job으로 감싼 함수를 호출하면, 함수가 반환한 “결과 딕셔너리”가 아니라 제출된 잡을 가리키는 AwsQuantumJob 객체(잡 핸들)가 반환됩니다. 이 핸들을 통해 잡의 고유 식별자(job.arn), 현재 상태(job.state()), 실행 로그(job.logs()), 그리고 완료 후 결과(job.result())에 접근할 수 있습니다. 즉 함수가 반환한 실제 결과(results, task summary, estimated cost)는 잡이 끝난 뒤 job.result()로 따로 가져오는 것이지, job에 바로 들어있는 것은 아닙니다.
print(f"{job.arn} {job.state()}")는 이 핸들을 이용해 방금 제출한 잡의 식별자와 현재 상태를 노트북에 출력하는 부분입니다. 잡 상태는 “QUEUED, RUNNING, COMPLETED, FAILED, CANCELLING, CANCELLED” 중 하나이며, 제출 직후에는 보통 QUEUED로 표시됩니다.
요약하면, 이 코드는 Amazon Braket 하이브리드 잡 기능을 활용해 자원 관리와 결과 저장(Amazon S3)을 자동화하고, Tracker로 실행 비용을 함께 추적하면서 양자-고전 하이브리드 알고리즘을 손쉽게 실행하도록 도와줍니다.
하이브리드 알고리즘 실행 결과 확인
아래 코드는 Amazon Braket 하이브리드 잡이 완료된 후, 그 결과와 실행 정보를 확인하는 예제입니다.
from pprint import pprint
result = job.result()
print(f"Create: {job.metadata()['createdAt']}")
print(f"Started: {job.metadata()['startedAt']}")
print(f"Ended: {job.metadata()['endedAt']}")
print()
print(f"Runtime of the job instance: {job.metadata()['billableDuration']} milliseconds")
print()
pprint(result['results'])
먼저, job.result()를 호출하여 하이브리드 잡의 실행 결과를 가져옵니다. 이어서 job.metadata() 메서드를 통해 잡의 다양한 메타데이터를 확인합니다. 이 메타데이터에는 잡이 생성된 시각(createdAt), 실제 실행이 시작된 시각(startedAt), 실행이 끝난 시각(endedAt)이 포함되어 있습니다. 각각의 시각 정보는 print문을 통해 사람이 읽기 쉬운 형태로 출력됩니다.
그 다음, 잡 인스턴스의 실제 과금 대상 실행 시간(billableDuration)도 밀리초 단위로 출력됩니다. 이 값은 AWS에서 해당 작업에 대해 요금이 부과되는 실제 실행 시간입니다. 마지막으로, pprint(result['results'])를 사용해 알고리즘 실행 결과(예: 각 반복에서의 회전 각도와 기댓값 등)를 보기 좋게 출력합니다. 이 코드를 통해 사용자는 하이브리드 잡의 생성, 시작, 종료 시각과 과금 대상 실행 시간, 그리고 알고리즘의 상세 결과를 한눈에 확인할 수 있으며, 그림6과 같은 결과를 주피터 노트북에서 바로 확인할 수 있습니다.

<그림6. Amazon Braket 하이브리드 잡 기능을 이용한 하이브리드 알고리즘의 실행 결과>
이 실행 결과는 그림7과 같이, Amazon Braket 콘솔의 ‘Hybrid jobs’에서도 확인 가능합니다. 알고리즘의 반복 횟수가 증가함에 따라 회전각의 값이 3.14에 수렴하고 이에 따라 ‘Z’ 연산자에 대한 기댓값이 -1로 수렴하는 것을 확인할 수 있습니다. 즉, 초기 |0⟩ 상태에서 ‘x’ 축으로 π만큼 이동하여 |1⟩로 이동한 것을 직관적으로 알 수 있습니다.

<그림7. Amazon Braket 콘솔에서 확인 가능한 하이브리드 알고리즘의 실행 결과>
또한 그림8과 같이 이 알고리즘을 수행하기 위해 11개의 양자 작업이 수행된 것도 ‘Hybrid jobs’ 내에서 확인 가능합니다.

<그림8. 하이브리드 알고리즘을 수행하기 위한 11개의 양자 작업 결과>
로컬 환경에서의 하이브리드 알고리즘 테스트
Amazon Braket에서 하이브리드 알고리즘을 개발할 때, 코드가 제대로 동작하는지 확인하려면 잡(Job)을 실행해봐야 합니다. 그런데 일반 하이브리드 잡은 AWS가 별도의 관리형 인스턴스를 준비(provisioning)하는 데 몇 분이 걸려, 짧은 디버깅을 반복하기에는 번거로울 수 있습니다.
이럴 때 유용한 것이 로컬 잡(Local Job)입니다. 로컬 잡은 별도 인스턴스를 띄우지 않고, 여러분의 코드가 실행되는 로컬 환경(내 컴퓨터나 주피터 노트북 등)의 로컬 Docker 컨테이너에서 바로 잡을 실행합니다. 그래서 provisioning 대기 없이 빠르게 실행하고 디버깅할 수 있습니다.
단, 로컬 잡을 실행하려면 Docker가 필요합니다. Docker는 프로그램을 격리된 환경(컨테이너)에서 실행하게 해주는 도구입니다. Amazon Braket이 제공하는 관리형 주피터 노트북(Braket notebooks)에는 Docker가 이미 설치되어 있어 별도 설치 없이 바로 로컬 잡을 실행할 수 있습니다. 반면 본인 컴퓨터에서 직접 실행하려면 Docker를 먼저 설치해야 합니다. 참고로 로컬 잡을 처음 실행할 때는 컨테이너 이미지를 준비하는 과정 때문에 시간이 더 걸릴 수 있지만, 한 번 준비되고 나면 이후 실행은 훨씬 빨라집니다.
아래 코드는 이 하이브리드 잡을 로컬 환경에서 실행하는 예시로, 데코레이터 한 줄(local=True 추가)을 제외하면 앞서 설명한 코드와 동일합니다.
@hybrid_job(device="arn:aws:braket:::device/quantum-simulator/amazon/sv1", local=True)
# @hybrid_job(device=Devices.Amazon.SV1)
def run_local_job(n_iterations, step_size, n_shots):
print("Execution started")
cost_tracker = Tracker().start()
device = AwsDevice(get_job_device_arn())
results = run_algorithm(device, n_iterations, step_size, n_shots)
cost_tracker.stop()
print("Execution completed")
return {
"results": results,
"task summary": cost_tracker.quantum_tasks_statistics(),
"estimated cost": float(cost_tracker.simulator_tasks_cost() + cost_tracker.qpu_tasks_cost()),
}
local_job = run_local_job(n_iterations=11, step_size=0.1, n_shots=1000)
print(f"{local_job.arn} {local_job.state()}")
먼저, @hybrid_job 데코레이터가 함수 run_local_job에 적용되어 있습니다. 이 데코레이터는 해당 함수가 하이브리드 양자-고전 알고리즘 작업임을 나타내며, device 파라미터로는 Amazon Braket의 양자 시뮬레이터인 SV1 디바이스가 앞선 코드와 동일하게 지정되어 있습니다. 또한 local=True 옵션을 통해, 이 작업이 로컬 환경에서 실행됨을 명시합니다. 이 옵션을 사용하면 고전적(클래식) 계산 부분만 로컬 리소스에서 실행되고, 양자 계산 부분(양자 회로 실행)은 여전히 지정된 AWS 리소스(예: SV1 시뮬레이터 등)에서 수행됩니다.
Amazon Braket에서 하이브리드 알고리즘을 로컬 잡 모드로 실행할 때의 주요 단점은 다음과 같습니다.
- 양자 작업에 대한 우선순위 없음: 로컬 잡 모드에서는 하이브리드 작업이 AWS의 클라우드 인프라에서 실행될 때와 달리, 양자 디바이스(QPU)나 시뮬레이터에 작업을 제출할 때 우선순위 대기열이 적용되지 않습니다. 즉, 하이브리드 작업의 양자 태스크가 일반 양자 태스크와 동일한 대기열에 들어가므로, 대기 시간이 길어질 수 있습니다.
- 성능상 이점 부재: 클라우드 하이브리드 잡은 AWS 인프라에서 실행되어 최적화된 환경과 예측 가능한 런타임, 빠른 실행 속도 등의 이점을 제공합니다. 반면, 로컬 잡은 사용자의 컴퓨터 환경(주피터 노트북 인스턴스, 데스크톱 등)에 따라 성능이 좌우되며, 대규모 연산이나 고성능이 필요한 경우 성능 저하가 발생할 수 있습니다.
- 관리형 모니터링 제한: 하이브리드 잡은 Amazon CloudWatch와 연동되어 실시간 지표 및 로그를 콘솔에서 확인할 수 있습니다. 반면 로컬 잡은 Amazon Braket 콘솔에 표시되지 않고 CloudWatch로 지표가 전송되지 않으므로, 콘솔 기반 모니터링은 사용할 수 없습니다. 대신 로컬 잡은 동기로 실행되며 로그와
log_metric()출력이 노트북(stdout)에 실시간으로 표시되어, 이를 통해 진행 상황을 확인합니다. - 대규모 분산 실행 제약: 멀티노드나 MPI 기반 노드 간 분산 컴퓨팅은 여러 고전 인스턴스를 할당하는 관리형 하이브리드 잡(instanceCount > 1)에서 지원됩니다. 로컬 잡은 내 머신의 단일 컨테이너에서 실행되므로 이러한 다중 노드 분산은 사용할 수 없으며, 병렬 처리도 로컬 자원 한도 내로 제한됩니다. 대규모 및 분산 실험이 필요하면 관리형 하이브리드 잡을 사용해야 합니다.
맺음말
이번 글에서는 “X축 회전 게이트 RX(θ)를 적용했을 때 Pauli Z 연산자의 기댓값이 회전각에 따라 어떻게 변하는가”라는 문제를, 고전 자원과 양자 자원을 함께 쓰는 하이브리드 알고리즘으로 풀어봤습니다. 수학적으로는 |0⟩ 상태에 RX(θ)를 적용하면 Z의 기댓값이 cos θ가 되어 -1에서 +1 사이를 오가게 되고, 이를 양자 토모그래피 관점에서 큐비트 상태로 해석할 수 있음을 확인했습니다.
이 문제를 Amazon Braket에서 구현하는 방법으로, 지난 글의 알고리즘 스크립트 방식에 이어 이번에는 @hybrid_job 데코레이터 방식을 다뤘습니다. 데코레이터로 감싼 함수를 호출하면 AWS가 관리형 인스턴스를 띄워 고전 연산(회전각 업데이트 및 반복 제어)을 수행하고, 회로 실행(양자 태스크)은 지정한 디바이스로 제출됩니다. 함수 호출은 결과가 아니라 AwsQuantumJob 핸들을 돌려주므로, job.state()로 상태(QUEUED, RUNNING, COMPLETED 등)를 확인하고 job.logs()나 콘솔의 Hybrid jobs 로그에서 실행 과정을, job.result()로 최종 결과를 확인할 수 있습니다.
또한 개발 단계에서는 별도 인스턴스 프로비저닝을 기다리지 않는 로컬 잡으로 코드를 빠르게 검증하고, 검증이 끝나면 관리형 하이브리드 잡으로 확장하는 흐름이 효율적입니다. 다만 로컬 잡에서도 회로 실행은 지정한 디바이스로 제출되며, 우선순위나 성능상의 이점은 관리형 하이브리드 잡 쪽에 있다는 점은 기억해 두는 게 좋습니다. 여기에 Tracker를 함께 사용하면 시뮬레이터 및 QPU 태스크의 예상 비용까지 추적할 수 있어, 인프라 관리보다 알고리즘 자체에 집중할 수 있습니다.
간단한 단일 큐비트 회전 예제였지만, “고전 루프가 양자 회로의 파라미터를 제어한다”는 이 구조는 변분 양자 알고리즘(VQE, QAOA 등) 같은 실제 하이브리드 워크로드로 그대로 확장됩니다. 이번 예제를 출발점 삼아, 여러분의 문제에 맞는 하이브리드 알고리즘을 직접 설계해 보시길 권합니다.